Files
RomhackPlaza/resources/js/SubmissionsClass/FSFileData.js

155 lines
3.8 KiB
JavaScript
Raw Normal View History

2026-05-20 18:25:15 +02:00
/** @typedef { import('types/UploadchunkResponse.js').UploadchunkResponse} UploadchunkResponse */
export const CHUNK_SIZE = 8192;
/**
* An uploaded file instance.
* Create a new file data.
*
* @param {string} name Filename
* @param {number} totalChunks Total number of chunks of the file.
* @param rawFile The JS file element relation.
*/
export function FSFileData(name, totalChunks, rawFile ) {
return {
/**
* Filename.
* @type {string}
*/
name,
/**
* Number of total chunks based on CHUNK_SIZE.
* @type {number}
*/
totalChunks,
/**
* The JS file element relation.
*/
rawFile,
/**
* Upload progression value.
* @type {number}
*/
progressValue: 0,
/**
* Current chunk uploaded.
* @type {number}
*/
currentChunk: 0,
/**
* If the upload of the file is finished.
* @type {boolean}
*/
done: false,
/**
* If there is an error during file uploading.
* @type {any|null}
*/
error: null,
/**
* UUID v4 for the file.
* @type {`${string}-${string}-${string}-${string}-${string}`}
*/
uuid: crypto.randomUUID(),
/**
* Look if this file is currently uploading.
* @returns {boolean}
*/
get isUploading()
{
return !this.done && !this.error;
},
/**
* Build API url.
* @param {string} section
* @returns {string} The API url.
*/
buildUrl(section)
{
return `/api/fs/upload-chunk/${section}`;
},
/**
* Upload the file.
* @param {string} section section of the file.
* @returns {Promise<void>}
*/
async upload(section)
{
if (!this.rawFile)
return; // Can't upload in that case.
/**
* Get CSRF token for uploading request.
* @type {string}
*/
const CSRF = document.querySelector('meta[name=csrf-token]')?.content ?? '';
for (let i = 0; i < this.totalChunks; i++) {
if (this.error)
return; // Abort the process.
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, this.rawFile.size);
const chunk = this.rawFile.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('file_uuid', this.uuid);
formData.append('current_chunk', i);
formData.append('total_chunks', this.totalChunks);
formData.append('filename', this.rawFile.name);
formData.append('_token', CSRF);
// -----
// UPLOAD TIME !
// -----
try {
const RESPONSE = await fetch(this.buildUrl(section), {method: 'POST', body: formData});
if (!RESPONSE.ok) // Problem with the request.
throw new Error(`${RESPONSE.status} ${RESPONSE.statusText}`);
/** @type {UploadchunkResponse} */
const DATA = await RESPONSE.json();
if (DATA.success !== true || DATA.uploaded !== true)
// The request reached the file server but could not be sent.
throw new Error(`${DATA.error}`);
this.currentChunk = i + 1;
this.progressValue = Math.round(((i + 1) / this.totalChunks) * 100);
if (DATA.finished === true) {
this.done = true;
return;
}
} catch (err) {
this.error = 'Error on chunk ' + (i + 1) + '. ' + err.message;
this.progressValue = 0;
return;
}
}
}
}
}