@ludovicm67/lib-filetransfer
Version:
Library to help building a file transfer application
187 lines (186 loc) • 6.17 kB
JavaScript
import { v4 as uuidv4 } from "uuid";
import { TransferFile } from "./TransferFile.js";
export class TransferFilePool {
transferFiles;
// configuration
maxBufferSize;
parallelCalls;
timeout = 1;
retries = 10;
// callbacks
askFilePartCallback;
constructor(options) {
this.transferFiles = {};
// manage askFilePartCallback
if (options?.askFilePartCallback) {
this.askFilePartCallback = options.askFilePartCallback;
}
else {
this.askFilePartCallback = (_fileId, _offset, _limit) => { };
}
this.maxBufferSize =
options?.maxBufferSize !== undefined ? options.maxBufferSize : 1000;
this.parallelCalls =
options?.parallelCalls !== undefined ? options.parallelCalls : 1;
if (options?.timeout !== undefined) {
this.timeout = options.timeout;
}
if (options?.retries !== undefined) {
this.retries = options.retries;
}
}
/**
* Check existance of a file in the pool.
*
* @param fileId Id of the file.
* @returns true if the file exists.
*/
fileExists(fileId) {
return Object.keys(this.transferFiles).includes(fileId);
}
/**
* Store file metadata.
*
* @param metadata File metadata.
* @returns The ID of the file.
*/
storeFileMetadata(metadata) {
// check presence of 'id' field
if (!metadata.id) {
throw new Error("no 'id' field");
}
// check presence of 'name' field
if (!metadata.name) {
throw new Error("no 'name' field");
}
// only store it if the file is not in the pool
if (!this.fileExists(metadata.id)) {
this.transferFiles[metadata.id] = new TransferFile(metadata.id, metadata.name, metadata.type || "application/octet-stream", metadata.size || 0, metadata.bufferLength || 0, this.timeout, this.retries);
}
return metadata.id;
}
/**
* Delete a file from the pool.
*
* @param fileId Id of the file.
*/
deleteFile(fileId) {
if (!this.fileExists(fileId)) {
return;
}
// remove keys with the specified fileId
this.transferFiles = Object.fromEntries(Object.entries(this.transferFiles).filter((x) => x[0] !== fileId));
}
/**
* Trigger the download of a file.
*
* @param fileId Id of the file.
* @param askFilePartCallback Callback function to ask a specific part of a file.
* @param parallelCalls Number of parallel calls to perform.
*/
async downloadFile(fileId, askFilePartCallback, parallelCalls) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
const file = this.transferFiles[fileId];
const calls = parallelCalls ? parallelCalls : this.parallelCalls;
if (askFilePartCallback !== undefined) {
await file.download(this.maxBufferSize, askFilePartCallback, calls);
}
else {
await file.download(this.maxBufferSize, this.askFilePartCallback, calls);
}
}
/**
* Abort the download of a file.
*
* @param fileId Id of the file.
*/
abortFileDownload(fileId) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
const file = this.transferFiles[fileId];
file.setDownloading(false);
}
/**
* Add a file directly to the pool.
*
* @param blob Blob to store to the pool.
* @param name Name of the file.
* @returns The metadata of the file.
*/
async addFile(blob, name) {
const fId = uuidv4();
if (this.fileExists(fId)) {
throw new Error("impossible to add this file to the pool, please retry");
}
const f = new TransferFile(fId, name, blob.type, blob.size, 0, this.timeout, this.retries);
await f.setBlob(blob);
this.transferFiles[fId] = f;
return f.getMetadata();
}
/**
* Read a specific part of a file.
*
* @param fileId Id of the file.
* @param offset From where to read.
* @param limit Maximum lenght of data we want to read.
* @returns ArrayBuffer containing the requested part of the file.
*/
readFilePart(fileId, offset, limit) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
return this.transferFiles[fileId].readFilePart(offset, limit);
}
/**
* Receive a specific part of a file.
*
* @param fileId Id of the file.
* @param offset From where it was read.
* @param limit Maximum length of read data.
* @param data ArrayBuffer containing the data of defined part of the file.
*/
receiveFilePart(fileId, offset, limit, data) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
this.transferFiles[fileId].receiveFilePart(offset, limit, data);
}
/**
* Get informations representing a specific file.
*
* @param fileId Id of the file.
* @returns Informations representing the requested file.
*/
getFile(fileId) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
return this.transferFiles[fileId].getFile();
}
/**
* Get the Blob of a specific complete file.
*
* @param fileId Id of the file.
* @returns The Blob of the complete file.
*/
getBlob(fileId) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
return this.transferFiles[fileId].getBlob();
}
/**
* Remove all the data of the file.
*
* @param fileId Id of the file.
*/
clearFile(fileId) {
if (!this.fileExists(fileId)) {
throw new Error(`file '#${fileId}' does not exist`);
}
this.transferFiles[fileId].clear();
}
}