UNPKG

@simplito/privmx-webendpoint

Version:

PrivMX Web Endpoint library

217 lines (216 loc) 7.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileUploader = exports.StreamReader = exports.FILE_DEFAULT_CHUNK_SIZE = void 0; exports.downloadFile = downloadFile; exports.FILE_DEFAULT_CHUNK_SIZE = 1_048_576; class StreamReader { _handle; _offset; _api; chunkSize; hasDataToRead = true; /** * Creates an instance of StreamReader. * * @param {number} handle - The file handle. * @param {StoreApi} api {@link StoreApi `StoreApi`} instance */ constructor(handle, api, chunkSize) { this._handle = handle; this._offset = 0; this._api = api; this.chunkSize = chunkSize ?? exports.FILE_DEFAULT_CHUNK_SIZE; } static async readFile(api, fileID, chunkSize) { const fileHandle = await api.openFile(fileID); const reader = new StreamReader(fileHandle, api, chunkSize); return reader; } /** * Reads the next chunk of the file. * * @returns {Promise<boolean>} A promise that resolves to true if there are more chunks to read, or false if the end of the file is reached. */ async *[Symbol.asyncIterator]() { while (this.hasDataToRead) { const chunk = await this.readNextChunk(); yield [chunk, this._offset]; } } async readNextChunk() { const chunkSizeToRead = this.chunkSize ?? exports.FILE_DEFAULT_CHUNK_SIZE; const chunk = await this._api.readFromFile(this._handle, chunkSizeToRead); //if chunk length is lesser than requested, the end of the file is reached this.hasDataToRead = chunk.length === chunkSizeToRead; return chunk; } async getFileContent() { const chunks = []; while (this.hasDataToRead) { chunks.push(await this.readNextChunk()); } await this.close(); const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); const fileBuffer = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { fileBuffer.set(chunk, offset); offset += chunk.length; } return fileBuffer; } /** * Aborts the reading process and closes the file handle. * * @returns {Promise<string>} A promise that resolves when the file handle is closed. */ async abort() { return this._api.closeFile(this._handle); } /** * Closes the file handle. * * @returns {Promise<string>} A promise that resolves when the file handle is closed and returns file ID. */ async close() { return this._api.closeFile(this._handle); } } exports.StreamReader = StreamReader; class FileUploader { _size; offset = 0; _api; _reader; /** * Creates an instance of FileUploader. * * @param {number} handle - The file handle. * @param {File} file - The data (file content) to upload. * @param {StoreApi} api {@link StoreApi `StoreApi`} instance */ constructor(file, api) { this._size = file.size; this._api = api; this._reader = file.stream().getReader(); } static async uploadStoreFile({ storeApi, storeId, file, privateMeta, publicMeta, }) { const meta = { publicMeta: publicMeta || new Uint8Array(), privateMeta: privateMeta || new Uint8Array(), }; const handle = await storeApi.createFile(storeId, meta.publicMeta, meta.privateMeta, file.size); const streamer = new FileUploader(file, { closeFile() { return storeApi.closeFile(handle); }, writeToFile(chunk) { return storeApi.writeToFile(handle, chunk); }, }); return streamer; } static async uploadInboxFile({ inboxApi, inboxHandle, preparedFileUpload, }) { const streamer = new FileUploader(preparedFileUpload.file, { closeFile() { return Promise.resolve(""); }, writeToFile(chunk) { return inboxApi.writeToFile(inboxHandle, preparedFileUpload.handle, chunk); }, }); return streamer; } static async prepareInboxUpload({ inboxApi, file, privateMeta, publicMeta, }) { const meta = { publicMeta: publicMeta || new Uint8Array(), privateMeta: privateMeta || new Uint8Array(), }; const handle = await inboxApi.createFileHandle(meta.publicMeta, meta.privateMeta, file.size); return { file: file, handle }; } /** * Gets the progress of uploading the file as a percentage. * * @returns {number} The progress percentage. */ get progress() { if (this._size === 0) return 100; return (this.offset / this._size) * 100; } /** * Sends the next chunk of the file data to the server. * * @returns {Promise<boolean>} A promise that resolves to true if there are more chunks to send, or false if all data has been sent. */ async sendNextChunk() { const { done, value } = await this._reader.read(); if (done) { return false; } await this._api.writeToFile(value); this.offset += value?.length; return true; } async uploadFileContent() { while (await this.sendNextChunk()) { } return this.close(); } /** * Aborts the uploading process, closes the file handle, and deletes the uploaded part of the file. * * @returns {Promise<void>} A promise that resolves when the file handle is closed and the uploaded part is deleted. */ async abort() { await this._api.closeFile(); } /** * Closes the file handle. * * @returns {Promise<string>} A promise that resolves when the file handle is closed and returns file ID. */ async close() { this._reader.releaseLock(); return this._api.closeFile(); } } exports.FileUploader = FileUploader; /** * Downloads a file from the server. * * @param {StoreApi|InboxApi} api - The API instance used for file operations. * @param {string} fileId - The ID of the file to download. * @param {string} [targetFileName] - The target file name for saving the downloaded file. * @returns {Promise<void>} A promise that resolves when the download is complete. */ async function downloadFile(api, fileId, targetFileName) { const filename = targetFileName || fileId; const apiReader = await StreamReader.readFile(api, fileId); if ("showSaveFilePicker" in window && window.isSecureContext) { //@ts-ignore const systemHandle = (await window.showSaveFilePicker({ id: 0, suggestedName: filename, startIn: "downloads", })); const accessHandle = await systemHandle.createWritable(); for await (const [file] of apiReader) { await accessHandle.write(file); } await accessHandle.close(); } else { const fileBuffer = await apiReader.getFileContent(); const anchor = document.createElement("a"); const reader = new FileReader(); reader.onload = (e) => { if (!e.target) return; anchor.href = e.target.result; anchor.download = filename; anchor.click(); }; reader.readAsDataURL(new Blob([fileBuffer])); } }