nodejs 上传下载文件

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);
    });
}
原文地址:https://www.cnblogs.com/zh33gl/p/13909527.html