UNPKG

@capgo/capacitor-uploader

Version:
218 lines (211 loc) 8.01 kB
'use strict'; var core = require('@capacitor/core'); var idb = require('idb'); const Uploader = core.registerPlugin('Uploader', { web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.UploaderWeb()), }); class PathHelper { // Check if the path follows the idb://[databaseName]/[storeName]/[key] format static isIndexedDBPath(path) { const regex = /^idb:\/\/([^/]+)\/([^/]+)\/(.+)$/; return regex.test(path); } // Parse the path to extract database, storeName, and key static parseIndexedDBPath(path) { const regex = /^idb:\/\/([^/]+)\/([^/]+)\/(.+)$/; const match = path.match(regex); if (!match) { throw new Error('Invalid IndexedDB path format'); } return { database: match[1], storeName: match[2], key: match[3], }; } } class UploaderWeb extends core.WebPlugin { constructor() { super(...arguments); this.uploads = new Map(); } async startUpload(options) { console.log('startUpload', options); const id = Math.random().toString(36).substring(2, 15); const controller = new AbortController(); const maxRetries = options.maxRetries || 3; this.uploads.set(id, { controller, retries: maxRetries }); this.doUpload(id, options); return { id }; } async uploadMultipart(options) { var _a, _b; return this.startUpload({ filePath: options.filePath, serverUrl: options.url, headers: (_a = options.headers) !== null && _a !== void 0 ? _a : {}, method: 'POST', uploadType: 'multipart', fileField: options.fieldName, parameters: (_b = options.fields) !== null && _b !== void 0 ? _b : {}, }); } async removeUpload(options) { console.log('removeUpload', options); const upload = this.uploads.get(options.id); if (upload) { upload.controller.abort(); this.uploads.delete(options.id); this.notifyListeners('events', { name: 'cancelled', id: options.id, payload: {}, }); } } async doUpload(id, options) { var _a, _b, _c; const { serverUrl, headers = {}, method = 'POST', parameters = {} } = options; const upload = this.uploads.get(id); if (!upload) return; try { const files = this.normalizeFiles(options); if (files.length === 0) throw new Error('Missing required parameter: filePath or files'); const resolvedMethod = method.toUpperCase(); const uploadType = (_a = options.uploadType) !== null && _a !== void 0 ? _a : (resolvedMethod === 'PUT' ? 'binary' : 'multipart'); let body; if (resolvedMethod === 'PUT' || uploadType === 'binary') { if (files.length !== 1) throw new Error('Binary uploads only support a single file'); const file = await this.getFileFromPath(files[0].filePath); if (!file) throw new Error('File not found'); body = file; } else { const formData = new FormData(); for (const fileOption of files) { const file = await this.getFileFromPath(fileOption.filePath); if (!file) throw new Error('File not found'); const fieldName = (_c = (_b = fileOption.fieldName) !== null && _b !== void 0 ? _b : options.fileField) !== null && _c !== void 0 ? _c : 'file'; formData.append(fieldName, file); } for (const [key, value] of Object.entries(parameters)) { formData.append(key, value); } body = formData; } const response = await fetch(serverUrl, { method: resolvedMethod, headers, body, signal: upload.controller.signal, }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); this.notifyListeners('events', { name: 'completed', id, payload: { statusCode: response.status }, }); this.uploads.delete(id); } catch (error) { if (error.name === 'AbortError') return; if (upload.retries > 0) { upload.retries--; console.log(`Retrying upload (retries left: ${upload.retries})`); setTimeout(() => this.doUpload(id, options), 1000); } else { this.notifyListeners('events', { name: 'failed', id, payload: { error: error.message }, }); this.uploads.delete(id); } } } normalizeFiles(options) { if (options.files && options.files.length > 0) return options.files; if (options.filePath) { return [ { filePath: options.filePath, fieldName: options.fileField, mimeType: options.mimeType, }, ]; } return []; } async getFileFromPath(filePath) { // Check if the path is an IndexedDB path if (PathHelper.isIndexedDBPath(filePath)) { return this.getFileFromIndexedDB(filePath); } // Otherwise, treat it as a file path from the system return this.getFileFromSystem(filePath); } // Retrieve the file from IndexedDB async getFileFromIndexedDB(filePath) { // Parse the path to get the database, store name, and key const { database, storeName, key } = PathHelper.parseIndexedDBPath(filePath); try { // Open the IndexedDB database and access the object store const db = await idb.openDB(database, 1, { upgrade(db) { if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName); } }, }); // Get the blob from the store const blob = await db.get(storeName, key); if (!blob) { console.error(`File with key "${key}" not found in store "${storeName}" in database "${database}"`); return null; } // Convert the Blob to a File object return new File([blob], key, { type: blob.type }); } catch (error) { console.error('Error retrieving file from IndexedDB:', error); return null; } } // Retrieve the file from the system (local file) async getFileFromSystem(filePath) { try { // This is a simplified version. In a real-world scenario, // you might need to handle different types of paths or use a file system API. const response = await fetch(filePath); const blob = await response.blob(); return new File([blob], filePath.split('/').pop() || 'file', { type: blob.type, }); } catch (error) { console.error('Error getting file from system:', error); return null; } } async acknowledgeEvent(_options) { // Web uploads run in-process; events are not persisted for replay. } async getPluginVersion() { return { version: 'web' }; } } var web = /*#__PURE__*/Object.freeze({ __proto__: null, UploaderWeb: UploaderWeb }); exports.Uploader = Uploader; //# sourceMappingURL=plugin.cjs.js.map