UNPKG

upload

Version:

Isomorphic TypeScript file upload library.

229 lines (228 loc) 8.34 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Upload = void 0; const form_data_1 = __importDefault(require("form-data")); class Upload { constructor(options) { this.events = { state: new Set(), error: new Set(), progress: new Set(), }; this.withCredentials = false; this._uploadedBytes = 0; this._totalBytes = 0; this._state = 'new'; if (!options) { throw new Error('Options are required.'); } if (!options.url || typeof options.url !== 'string') { throw new Error('Destination URL is missing or invalid.'); } this.form = options.form; this.url = options.url; this.headers = options.headers; this.withCredentials = options.withCredentials; } /** * POSTs the form. */ upload() { return new Promise((resolve, reject) => { // Check if we're running in a browser. if (typeof window !== 'undefined' && typeof XMLHttpRequest !== 'undefined') { this.xhr = new XMLHttpRequest(); if (this.withCredentials) { this.xhr.withCredentials = true; } this.xhr.open('POST', this.url, true); if (typeof this.headers === 'object') { for (const headerName of Object.keys(this.headers)) { this.xhr.setRequestHeader(headerName, this.headers[headerName]); } } this.xhr.addEventListener('loadstart', () => { this.setState('started'); }); if (this.xhr.upload) { this.xhr.upload.addEventListener('progress', e => { if (this._totalBytes !== e.total) { this.setTotalBytes(e.total); } this.setUploadedBytes(e.loaded); }); } this.xhr.addEventListener('load', () => { if (this.xhr) { this.setUploadedBytes(this.totalBytes); this.setState('successful'); const response = {}; const lines = this.xhr .getAllResponseHeaders() .replace(/\r/g, '') .split('\n'); const headers = {}; for (const line of lines) { const split = line.split(':'); if (split.length != 2) { continue; } headers[split[0].trim()] = split[1].trim(); } response.headers = headers; response.status = this.xhr.status; response.xhr = this.xhr; switch (this.xhr.responseType) { case 'json': response.data = JSON.stringify(this.xhr.response); break; default: response.data = this.xhr.response; } resolve(response); } }); this.xhr.addEventListener('error', () => { this.setState('failed'); this.emit('error'); reject(); }); this.xhr.addEventListener('abort', () => { this.setState('aborted'); }); if (this.form instanceof FormData) { this.xhr.send(this.form); } else { const form = this.form; const formData = new FormData(); for (const key of Object.keys(this.form)) { formData.set(key, form[key]); } this.xhr.send(formData); } } else { const callback = (error, res) => { if (error) { this.setState('failed'); this.emit('error'); reject(); } else { this.setUploadedBytes(this.totalBytes); this.setState('successful'); let body = ''; res.on('readable', () => { const chunk = res.read(); if (chunk) { body += chunk; } }); res.on('end', () => { const response = {}; response.data = body; response.headers = res.headers; resolve(response); }); } }; const url = new URL(this.url); const options = { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST', headers: this.headers, }; let formData; if (this.form instanceof form_data_1.default) { formData = this.form; } else { const form = this.form; formData = new form_data_1.default(); for (const key of Object.keys(this.form)) { formData.append(key, form[key]); } } formData.getLength((error, length) => { this.setTotalBytes(length); }); formData.on('data', chunk => { if (this.state === 'new') { this.setState('started'); } if (chunk.hasOwnProperty('length')) { this.increaseUploadedBytes(chunk.length); } }); formData.submit(options, callback); } }); } abort() { var _a; (_a = this.xhr) === null || _a === void 0 ? void 0 : _a.abort(); } get uploadedBytes() { return this._uploadedBytes; } setUploadedBytes(value) { this._uploadedBytes = value; this.emit('progress', this.progress); } increaseUploadedBytes(value) { this._uploadedBytes += value; this.emit('progress', this.progress); } get totalBytes() { return this._totalBytes; } setTotalBytes(value) { this._totalBytes = value; this.emit('progress', this.progress); } /** * Current upload progress. A float between 0 and 1. */ get progress() { return this._totalBytes === 0 ? 0 : this._uploadedBytes / this._totalBytes; } get state() { return this._state; } setState(value) { const oldState = this._state; this._state = value; if (oldState !== this._state) { this.emit('state', this._state); } } /** * Adds a listener for a given event. * @param eventType Event type. * @param listener Listener function. */ on(eventType, listener) { this.events[eventType].add(listener); } /** * Removes a listener for a given event. * @param eventType Event type. * @param listener Listener function. */ off(eventType, listener) { this.events[eventType].delete(listener); } emit(eventType, ...args) { for (const listener of this.events[eventType]) { listener.apply(this, args); } } } exports.Upload = Upload;