2026-03-17 12:06:17 +00:00

173 lines
4.6 KiB
JavaScript

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}`;
}
}