nodejs 上傳下載檔案
阿新 • • 發佈:2020-11-01
import { stat, mkdir, createWriteStream, createReadStream, unlink } from 'fs'; import { parse as pathParser, extname, join as pathJoin } from 'path'; import { request, get } from 'http'; import { logger } from '@helper/helper'; /** * uploadUrl = `http://${address}:${port}/xnode/file/ul?access_token=${token}`; downloadUrl = `http://${address}:${port}/xnode/file/dl?access_token=${token}$media_id=`; */ async function createDownloadDir(dir) { const opts = process.platform == 'win32' ? { recursive: true } : {}; return new Promise((resolve, reject) => { mkdir(dir, opts, err => { return err ? reject(err) : resolve(dir); }) }); } async function getFileStats(filePath) { return new Promise((resolve, reject) => { stat(filePath, (err, stats) => { return err ? reject(err) : resolve(stats); }) }); } class FileManager { constructor() { this._downloadDir = process.cwd() + '/download'; } init(opts = {}) { opts = Object.assign({ port: 8099 }, opts); this._opts = opts; this._downloadDir = opts.downloadDir || process.cwd() + '/download'; this._uploadUrl = `http://${opts.address}:${opts.port}/xnode/file/ul?access_token=${opts.token}`; this._downloadUrl = `http://${opts.address}:${opts.port}/xnode/file/dl?access_token=${opts.token}&media_id=`; //this._createDownloadDir(this._downloadDir); return this; } /** * 上傳檔案 * @param {String} filePath 檔案路徑 E:\\Project\\cccs\\cccc\\public\\static\\imgs\\app.png * @param {Function} onProgress */ async upload(filePath, onProgress) { const fd = pathParser(filePath); const fileName = fd.base; const stats = await getFileStats(filePath); const cb = onProgress && typeof onProgress == 'function' ? onProgress : null; let totalLength = stats.size; const opts = { method: 'POST', headers: { 'Content-disposition': `attachment; filename="${fileName}"`, 'Content-type': 'application/octet-stream', // 二進位制資料流 'Content-length': totalLength, 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked', // 分片傳輸 } }; return new Promise((resolve, reject) => { let chunkLength = 0; let totalProgress = 0; let currProgress = 0; let req = request(this._uploadUrl.replace('8099', '8099'), opts, res => { let data = []; res.on('data', chunk => { data.push(chunk); }).on('end', () => { req = null; data = data.join(); return resolve(JSON.parse(data)); }); }); req.on('socket', socket => { socket.on('connect', () => { let rs = createReadStream(filePath); rs.on('data', chunk => { chunkLength += chunk.length; currProgress = (chunkLength / totalLength * 100) | 0; if (currProgress != totalProgress) { totalProgress = currProgress; totalProgress % 5 == 0 && cb && cb(totalProgress); } }).on('end', () => { rs.close(); rs = null; req.end(); }); rs.pipe(req); }); }) req.on('timeout', () => { return reject('timeout'); }).on('error', err => { return reject(err); }); }); } /** * 下載檔案 * @param {String} fileId 檔案ID * @param {Function} onProgress */ async download(fileId, onProgress) { const dir = await createDownloadDir(this._downloadDir); const cb = onProgress && typeof onProgress == 'function' ? onProgress : null; return new Promise((resolve, reject) => { const url = this._downloadUrl + fileId; let req = request(url, res => { console.log(res) //content-disposition: "attachment; filename="IMG_20200610_143528.jpg"" const totalLength = res.headers['content-length']; const fileName = res.headers['content-disposition'].split('=')[1].replace(/"/g, ''); const extName = extname(fileName); const dest = pathJoin(dir, `${fileId}${extName}`); let out = createWriteStream(dest); if (res.statusCode != 200) { } let chunkLength = 0; let totalProgress = 0; let currProgress = 0; res.on('data', chunk => { chunkLength += chunk.length; currProgress = (chunkLength / totalLength * 100) | 0; if (currProgress != totalProgress) { totalProgress = currProgress; totalProgress % 5 == 0 && cb && cb(totalProgress); } }).on('end', () => { }); out.on('finish', () => { req = null; out.close(); out = null; resolve({ attachName: fileName, attachPath: dest, attachSize: totalLength }); }).on('error', err => { unlink(dest, err => { }); reject(err); }); res.pipe(out); }); req.on('timeout', () => { return reject('timeout'); }).on('error', err => { return reject('error'); }); req.end(); }); } } export default new FileManager(); function fetchProgress(url, opts = {}, onProgress) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open(opts.method || 'get', url); for (let key in opts.headers || {}) { xhr.setRequestHeader(key, opts.headers[key]); } xhr.onload = e => resolve(e.target.responseText); xhr.onerror = reject; if (xhr.upload && onProgress) { xhr.upload.onprogress = onProgress; //上傳 } if ('onprogerss' in xhr && onProgress) { xhr.onprogress = onProgress; //下載 } xhr.send(opts.body); }); }