import { v4 as uuidv4 } from 'uuid'; import Axios from "axios"; import {baseURLApi} from "../../config"; function extractExtensionFrom(filename) { if (!filename) { return null; } const regex = /(?:\.([^.]+))?$/; return regex.exec(filename)[1]; } export default class FileUploader { static validate(file, schema) { if (!schema) { return; } if (schema.image) { if (!file.type.startsWith("image")) { throw new Error("You must upload an image"); } } if (schema.size && file.size > schema.size) { throw new Error("File is too big."); } const extension = extractExtensionFrom(file.name); if (schema.formats && !schema.formats.includes(extension)) { throw new Error("Invalid format"); } } static async upload(path, file, schema) { try { this.validate(file, schema); } catch (error) { return Promise.reject(error); } const extension = extractExtensionFrom(file.name); const id = uuidv4(); const filename = `${id}.${extension}`; const privateUrl = `${path}/${filename}`; const publicUrl = await this.uploadToServer(file, path, filename); return { id: id, name: file.name, sizeInBytes: file.size, privateUrl, publicUrl, new: true, }; } static async uploadChunked(path, file, schema, options = {}) { try { this.validate(file, schema); } catch (error) { return Promise.reject(error); } const chunkSize = Number(options.chunkSize) > 0 ? Number(options.chunkSize) : 5 * 1024 * 1024; const maxRetries = Number(options.maxRetries) > 0 ? Number(options.maxRetries) : 3; const onProgress = typeof options.onProgress === 'function' ? options.onProgress : null; const onStatus = typeof options.onStatus === 'function' ? options.onStatus : null; const extension = extractExtensionFrom(file.name); const id = uuidv4(); const filename = extension ? `${id}.${extension}` : id; const privateUrl = `${path}/${filename}`; const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize)); const initResponse = await Axios.post('/file/upload-sessions/init', { folder: path, filename, size: file.size, contentType: file.type || '', totalChunks, }); const sessionId = initResponse?.data?.sessionId; if (!sessionId) { throw new Error('Upload session was not created'); } for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex += 1) { const start = chunkIndex * chunkSize; const end = Math.min(file.size, start + chunkSize); const chunk = file.slice(start, end); if (onStatus) { onStatus('uploading', { chunkIndex, totalChunks, }); } let retry = 0; while (retry <= maxRetries) { try { await Axios.put(`/file/upload-sessions/${sessionId}/chunks/${chunkIndex}`, chunk, { headers: { 'Content-Type': 'application/octet-stream', }, }); break; } catch (error) { retry += 1; if (retry > maxRetries) { throw error; } await new Promise((resolve) => setTimeout(resolve, 500 * retry)); } } if (onProgress) { const percent = Math.round(((chunkIndex + 1) / totalChunks) * 100); onProgress(percent, { chunkIndex: chunkIndex + 1, totalChunks, }); } } if (onStatus) { onStatus('finalizing', null); } const finalizeResponse = await Axios.post(`/file/upload-sessions/${sessionId}/finalize`); const responsePublicUrl = finalizeResponse?.data?.url; const publicUrl = responsePublicUrl ? responsePublicUrl.startsWith('http') ? responsePublicUrl : `${baseURLApi}${responsePublicUrl}` : `${baseURLApi}/file/download?privateUrl=${encodeURIComponent(privateUrl)}`; return { id, name: file.name, sizeInBytes: file.size, privateUrl, publicUrl, new: true, }; } static async uploadToServer(file, path, filename) { const formData = new FormData(); formData.append("file", file); formData.append("filename", filename); const uri = `/file/upload/${path}`; await Axios.post(uri, formData, { headers: { "Content-Type": "multipart/form-data", }, }); const privateUrl = `${path}/${filename}`; console.log("process.env.NODE_ENV in uploadToServer function", process.env.NODE_ENV); console.log("baseURLApi in uploadToServer function", baseURLApi); return `${baseURLApi}/file/download?privateUrl=${privateUrl}`; } }