105 lines
3.8 KiB
JavaScript
105 lines
3.8 KiB
JavaScript
import {createIcons, icons} from "lucide";
|
|
export const CHUNK_SIZE = 8192;
|
|
export function FSUploader(){
|
|
return {
|
|
files: [],
|
|
rawFiles: [],
|
|
section: document.querySelector("meta[name='fs-section']")?.content ?? '',
|
|
init( oldFiles ){
|
|
this.$watch('files', () =>{
|
|
this.$nextTick(() => window.refreshIcons(this.$el) )
|
|
})
|
|
},
|
|
get isUploading(){
|
|
return this.files.some(f => !f.done && !f.error);
|
|
},
|
|
get hasErrors(){
|
|
return this.files.some(f => f.error);
|
|
},
|
|
get allUploaded(){
|
|
return this.files.length === 0 || this.files.every(f => f.done);
|
|
},
|
|
async submitFile(e){
|
|
const selected = Array.from(e.target.files);
|
|
e.target.value = '';
|
|
|
|
for( const raw of selected ){
|
|
const totalChunks = Math.ceil(raw.size / CHUNK_SIZE );
|
|
const index = this.files.length;
|
|
this.files.push({
|
|
name: raw.name,
|
|
progress: 0,
|
|
currentChunk: 0,
|
|
totalChunks: totalChunks,
|
|
done: false,
|
|
error: null,
|
|
uuid: crypto.randomUUID()
|
|
});
|
|
this.rawFiles.push(raw);
|
|
this.uploadChunks(raw,index);
|
|
}
|
|
},
|
|
async uploadChunks(rawFile, index){
|
|
const file = this.files[index];
|
|
const url = `/api/fs/upload-chunk/${this.section}`;
|
|
const csrf = document.querySelector('meta[name=csrf-token]')?.content ?? '';
|
|
|
|
for( let i = 0; i < file.totalChunks; i++ ){
|
|
if( file.error )
|
|
return;
|
|
|
|
const start = i * CHUNK_SIZE;
|
|
const end = Math.min(start + CHUNK_SIZE, rawFile.size);
|
|
const chunk = rawFile.slice(start, end);
|
|
const formData = new FormData();
|
|
|
|
formData.append('file', chunk);
|
|
formData.append('file_uuid', file.uuid);
|
|
formData.append('current_chunk', i);
|
|
formData.append('total_chunks', file.totalChunks);
|
|
formData.append( 'filename', rawFile.name );
|
|
formData.append('_token', csrf );
|
|
|
|
try {
|
|
const response = await fetch(url, { method: 'POST', body: formData });
|
|
if(!response.ok)
|
|
throw new Error(`${response.status} ${response.statusText}`);
|
|
|
|
const data = await response.json();
|
|
if( data.success !== true || data.uploaded !== true )
|
|
throw new Error(`${data.error}`);
|
|
|
|
file.currentChunk = i + 1;
|
|
file.progress = Math.round((( i + 1) / file.totalChunks ) * 100);
|
|
|
|
if( data.finished === true ){
|
|
file.done = true;
|
|
}
|
|
} catch(err){
|
|
file.error = 'Error on chunk ' + ( i + 1 ) + '. ' + err.message;
|
|
file.progress = 0;
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
retry(index){
|
|
const rawFile = this.rawFiles[index];
|
|
const totalChunks = Math.ceil(rawFile.size / CHUNK_SIZE );
|
|
this.files[index] = {
|
|
name: rawFile.name,
|
|
progress: 0,
|
|
currentChunk: 0,
|
|
totalChunks: totalChunks,
|
|
done: false,
|
|
error: null,
|
|
uuid: crypto.randomUUID()
|
|
};
|
|
this.uploadChunks(raw,index);
|
|
},
|
|
remove(index){
|
|
this.files.splice(index, 1);
|
|
this.rawFiles.splice(index, 1);
|
|
}
|
|
}
|
|
}
|