224 lines
6.3 KiB
Vue
224 lines
6.3 KiB
Vue
<template>
|
|
<q-card flat class="card">
|
|
<q-card-section>
|
|
<div class="text-h6">Home</div>
|
|
</q-card-section>
|
|
<q-card-section class="q-pt-none">
|
|
<div class="q-pa-none">
|
|
<q-table title="File system" :rows="data.file_list" :columns="columns" row-key="name" selection="single"
|
|
v-model:selected="selected" :flat="true" :bordered="false" :rows-per-page-options="[10, 20, 30, 0]" />
|
|
</div>
|
|
<div class="q-pa-md">
|
|
<div><q-file v-model="file" label="Select file to upload" /></div>
|
|
</div>
|
|
<div class="q-pa-md">
|
|
<q-btn class="btn q-ma-xs" flat label="DOWNLOAD" @click="DownloadFile"></q-btn>
|
|
<q-btn class="btn q-ma-xs" flat label="UPLOAD" @click="UploadFile"></q-btn>
|
|
<q-btn class="btn q-ma-xs" flat label="DELETE" @click="DeleteFile"></q-btn>
|
|
<q-btn class="btn q-ma-xs" flat label="TEST SAVE" @click="SaveFile"></q-btn>
|
|
</div>
|
|
<div class="row q-pa-md">
|
|
|
|
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, onUnmounted, reactive, onMounted, ref } from "vue";
|
|
import { PostData } from "components/webguicomp//network";
|
|
import { secondsToHms } from "components/webguicomp/helpers"
|
|
import { useQuasar, Dialog } from 'quasar';
|
|
const $q = useQuasar();
|
|
|
|
const columns = [
|
|
{ name: 'fname', required: true, label: 'File name', align: 'left', field: 'name', sortable: true },
|
|
{ name: 'fsize', label: 'Size ', align: 'left', field: 'size' }
|
|
]
|
|
|
|
defineOptions({
|
|
name: 'FilesCard'
|
|
})
|
|
|
|
const BLOCK_SIZE = 5120;
|
|
const init_list = [
|
|
{
|
|
sel: false,
|
|
name: '',
|
|
size: '',
|
|
}
|
|
]
|
|
|
|
async function SaveFile(data, fn) {
|
|
let blobData = new Blob([data]);
|
|
let url = window.URL.createObjectURL(blobData);
|
|
let a = document.createElement("a");
|
|
a.style = "display: none";
|
|
document.body.appendChild(a);
|
|
a.href = url;
|
|
a.download = fn;
|
|
a.click();
|
|
window.URL.revokeObjectURL(urlFile);
|
|
a.remove();
|
|
}
|
|
|
|
function ReceiveChunk(cur, total, chunk) {
|
|
let foper;
|
|
if (total == 1) foper = 3;
|
|
else if (cur == 0) foper = 1;
|
|
else if (cur == (total - 1)) foper = 2;
|
|
else foper = 0;
|
|
let data = {
|
|
raw_data: {
|
|
opertype: 1,
|
|
operphase: foper,
|
|
mem_object: selected.value[0].name,
|
|
size: BLOCK_SIZE,
|
|
dat: ''
|
|
}
|
|
};
|
|
return new Promise((resolve, reject) => {
|
|
console.log(`Receive chunk ${cur} from ${total}`)
|
|
PostData(data, 1, 0, () => resolve(data))
|
|
})
|
|
}
|
|
|
|
|
|
function base64ToArrayBuffer(base64) {
|
|
var binaryString = atob(base64);
|
|
var bytes = new Uint8Array(binaryString.length);
|
|
for (var i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
async function ReveiveFileChunks(buf) {
|
|
let chunksnum = Math.floor(selected.value[0].size / BLOCK_SIZE);
|
|
if (selected.value[0].size % BLOCK_SIZE)
|
|
chunksnum++;
|
|
console.log(`Found ${chunksnum} blocks in file ${selected.value[0].name}`)
|
|
let i;
|
|
let chunk, resp;
|
|
const dialog = Dialog.create({ message: 'Downloaded 0%', progress: true, persistent: true, ok: false, style: 'border: none; box-shadow: none;' })
|
|
for (i = 0; i < chunksnum; i++) {
|
|
resp = await ReceiveChunk(i, chunksnum, chunk);
|
|
let decoded = base64ToArrayBuffer(resp.raw_data.dat);
|
|
for (let k = 0; k < decoded.byteLength; k++)
|
|
buf[i * BLOCK_SIZE + k] = decoded[k];
|
|
dialog.update({ message: `Downloaded ${Math.floor(i * 100 / chunksnum)}%` })
|
|
}
|
|
dialog.hide();
|
|
}
|
|
|
|
async function DownloadFile() {
|
|
if (selected.value[0]) {
|
|
const buf = new Uint8Array(selected.value[0].size);
|
|
await ReveiveFileChunks(buf);
|
|
SaveFile(buf, selected.value[0].name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function ToBase64(bytes) {
|
|
var binary = '';
|
|
var len = bytes.byteLength;
|
|
for (var i = 0; i < len; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return window.btoa(binary);
|
|
}
|
|
|
|
function SendChunk(cur, total, reader) {
|
|
return new Promise((resolve, reject) => {
|
|
let arr;
|
|
if (cur == (total - 1))
|
|
arr = new Uint8Array(reader.result, cur * BLOCK_SIZE);
|
|
else
|
|
arr = new Uint8Array(reader.result, cur * BLOCK_SIZE, BLOCK_SIZE);
|
|
let encode = ToBase64(arr);
|
|
let length = encode.length;
|
|
|
|
let foper;
|
|
if (total == 1) foper = 3;
|
|
else if (cur == 0) foper = 1;
|
|
else if (cur == (total - 1)) foper = 2;
|
|
else foper = 0;
|
|
|
|
let data = {
|
|
raw_data: {
|
|
opertype: 3,
|
|
operphase: foper,
|
|
mem_object: file.value.name,
|
|
size: length,
|
|
dat: encode
|
|
}
|
|
};
|
|
|
|
console.log(`Send chunk ${cur} from ${total} length ${arr.byteLength}`)
|
|
PostData(data, 1, 0, () => resolve())
|
|
})
|
|
}
|
|
|
|
async function SendFileChunks(reader) {
|
|
let chunksnum = Math.floor(reader.result.byteLength / BLOCK_SIZE);
|
|
if (reader.result.byteLength % BLOCK_SIZE)
|
|
chunksnum++;
|
|
console.log(`Found ${chunksnum} blocks in file`)
|
|
let i;
|
|
const dialog = Dialog.create({ message: 'Uploaded 0%', progress: true, persistent: true, ok: false, style: 'border: none; box-shadow: none;' })
|
|
for (i = 0; i < chunksnum; i++) {
|
|
await SendChunk(i, chunksnum, reader);
|
|
dialog.update({ message: `Uploaded ${Math.floor(i * 100 / chunksnum)}%` })
|
|
}
|
|
dialog.update({ message: 'Done.Refreshing file list...' });
|
|
PostData(data, 2, 0, () => { dialog.hide() });
|
|
}
|
|
|
|
function UploadFile() {
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
console.log(`File "${file.value.name}" with size ${file.value.size} byte`);
|
|
if (reader.result.byteLength == 0)
|
|
return;
|
|
SendFileChunks(reader);
|
|
}
|
|
reader.readAsArrayBuffer(file.value);
|
|
}
|
|
|
|
function DeleteFile() {
|
|
if (selected.value[0]) {
|
|
$q.dialog({
|
|
title: 'Confirm delete file',
|
|
message: `Are you really wan to delete file "${selected.value[0].name}"?`,
|
|
cancel: true,
|
|
persistent: true
|
|
}).onOk(() => {
|
|
const dialog = Dialog.create({ message: 'Waiting for delete finished...', progress: true, persistent: true, ok: false, style: 'border: none; box-shadow: none;' })
|
|
|
|
PostData({
|
|
raw_data: {
|
|
opertype: 2,
|
|
operphase: 3,
|
|
mem_object: selected.value[0].name,
|
|
offset: 0,
|
|
size: 0
|
|
}
|
|
}, 1, 0, () => { PostData(data, 2, 0, () => { dialog.hide(); }) })
|
|
})
|
|
}
|
|
}
|
|
|
|
const init = {
|
|
file_list: [init_list]
|
|
}
|
|
|
|
const data = reactive(init);
|
|
const selected = ref([]);
|
|
const file = ref(null);
|
|
|
|
PostData(data, 2, 0, null);
|
|
</script>
|