cista-storage/cista/static/index.html

155 lines
4.1 KiB
HTML

<!DOCTYPE html>
<title>Storage</title>
<div>
<h2>Quick file upload</h2>
<p>Uses parallel WebSocket connections for increased bandwidth /api/upload</p>
<input type=file id=fileInput>
<progress id=progressBar value=0 max=1></progress>
</div>
<div>
<h2>File downloads (websocket)</h2>
<ul id=file_list></ul>
</div>
<h2>File listings</h2>
<p>Plain HTML browser <a href=/files/>/files/</a></p>
<p>JSON list updated via WebSocket /api/watch:</p>
<textarea id=list style="padding: 1em; width: 80ch; height: 40ch;"></textarea>
<script>
const list = document.getElementById("list")
let files = {}
function createWatchSocket() {
const wsurl = new URL("/api/watch", location.href.replace(/^http/, 'ws'))
const ws = new WebSocket(wsurl)
ws.onmessage = event => {
msg = JSON.parse(event.data)
console.log("Watch", msg)
if (msg.root) {
files = msg.root
file_list(files)
} else if (msg.update) {
const {path, data} = msg.update
for (const p of path.split("/")) {
// TODO update files at path with new data
}
}
list.value = JSON.stringify(files)
}
}
function file_list(files) {
const ul = document.getElementById("file_list")
ul.innerHTML = ""
const dir = ""
let ptr = files.dir
console.log(ptr)
for (const name of Object.keys(ptr)) {
if (ptr[name].dir) continue
const {size, mtime} = ptr[name]
const li = document.createElement("li")
const a = document.createElement("a")
ul.appendChild(li)
li.appendChild(a)
a.textContent = name
a.href = name
a.onclick = event => {
event.preventDefault()
download(name, size)
}
}
}
createWatchSocket()
async function download(name, size) {
const wsurl = new URL("/api/download", location.href.replace(/^http/, 'ws'))
const ws = new WebSocket(wsurl)
ws.binaryType = 'arraybuffer'
ws.onopen = () => {
console.log("Download socket connected")
ws.send(JSON.stringify({name, start: 0, end: size, size}))
}
ws.onmessage = event => {
const data = event.data
console.log("Download", data)
const blob = new Blob([data], {type: "application/octet-stream"})
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = name
a.click()
ws.close()
}
ws.onclose = () => {
console.log("Download socket disconnected")
}
}
const fileInput = document.getElementById("fileInput")
const progress = document.getElementById("progressBar")
const numConnections = 2
const chunkSize = 1<<20
const wsConnections = new Set()
for (let i = 0; i < numConnections; i++) createUploadWS()
function createUploadWS() {
const wsurl = new URL("/api/upload", location.href.replace(/^http/, 'ws'))
const ws = new WebSocket(wsurl)
ws.binaryType = 'arraybuffer'
ws.onopen = () => {
wsConnections.add(ws)
console.log("Upload socket connected")
}
ws.onmessage = event => {
msg = JSON.parse(event.data)
if (msg.written) progress.value += +msg.written
else console.log(`Error: ${msg.error}`)
}
ws.onclose = () => {
wsConnections.delete(ws)
console.log("Upload socket disconnected, reconnecting...")
setTimeout(createUploadWS, 1000)
}
}
async function load(file, start, end) {
const reader = new FileReader()
const load = new Promise(resolve => reader.onload = resolve)
reader.readAsArrayBuffer(file.slice(start, end))
const event = await load
return event.target.result
}
async function sendChunk(file, start, end, ws) {
const chunk = await load(file, start, end)
ws.send(JSON.stringify({
name: file.name,
size: file.size,
start: start,
end: end
}))
ws.send(chunk)
}
fileInput.addEventListener("change", async function() {
const file = this.files[0]
const numChunks = Math.ceil(file.size / chunkSize)
progress.value = 0
progress.max = file.size
console.log(wsConnections)
for (let i = 0; i < numChunks; i++) {
const ws = Array.from(wsConnections)[i % wsConnections.size]
const start = i * chunkSize
const end = Math.min(file.size, start + chunkSize)
const res = await sendChunk(file, start, end, ws)
}
})
</script>