• Skip to primary navigation
  • Skip to content
  • Skip to primary sidebar

陈文管的博客

分享有价值的内容

  • Android
  • Affiliate
  • SEO
  • 前后端
  • 网站建设
  • 自动化
  • 开发资源
  • 关于

Node.js后端文件上传、文件接收保存及文件下载实现

2020年12月27日 | 最近更新于 下午10:19

直接给下Node.js后端两种文件上传方式、后端服务接收保存文件,以及后端文件下载的具体代码实现。​

文章目录

  • 一、Node.js后端文件上传
    • 1)使用FormData上传文件
    • 2)自定义boundary参数上传文件
  • 二、Node.js后端文件接收及保存
  • 三、Node.js后端文件下载

一、Node.js后端文件上传

这边注意下,如果出现以下错误

Error: socket hang up

检查下上传的后端接口是不是用https,之后修改下引入的http对象为https。

1)使用FormData上传文件

var FormData = require('form-data')
//如果后端用的是https,这边引入的是http就会报socket hang up异常。
var http = require('https');

//调用示例:
let form = new FormData();
var uploadLoadFilePath = "../../test.zip"
form.append("filename", fs.createReadStream(uploadLoadFilePath))
uploadRequest("test.cn", "8080" '/api/upload/file', form, 'POST').then(function (result) {
}).catch(function (err) {
})

/**
 * 上传文件或者请求数据都可以用这个方法处理
 */
function uploadRequest(host, port, path, body, method){
  return new Promise(function (resolve, reject) {
    var option = {
      hostname: host,
      port: port,
      path: path,
      method: method ? method : 'GET',
      headers: {}
    };
    var data = null;
    if (body) {
      if (body instanceof FormData) {
        option.method = 'POST';
        option.headers = body.getHeaders()
        data = body
      } else {
        if (typeof(body) == 'string') {
          data = body
        } else {
          data = JSON.stringify(body)
        }
        option.method = 'POST';
        option.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
        option.headers["Content-Type"] = 'application/json';
      }
    }
    if (method) {
      option.method = method;
    }
    var req = http.request(option, function (res) {
      var body = '';
      res.on('data', function (chunk) {
        body += chunk;
      });
      res.on('end', function () {
        console.log("coverage coverageRequest request body:%s", body)
        resolve(JSON.parse(body));
      })
    });
    req.on('error', function (e) {
      console.log("coverage coverageRequest request error:%s", e)
      reject(e)
    });
    if (data instanceof FormData) {
      data.pipe(req)
    } else {
      if (data != null) {
        req.write(data);
      }
      req.end();
    }
  });
}

2)自定义boundary参数上传文件

// 示例调用
let body = {
  "testType": testType,
  "flag": 'full_test'
}
var savePath = "../../test.zip"
uploadFiles("test.cn", '/api/upload/file', "8080",
  [{urlKey: 'testData', urlValue: JSON.stringify(body)}],
  [{urlKey: 'file', urlValue: savePath}]).then(function (result) {
  console.log("uploadFile result:%s", result)
}).catch(function (err) {
  console.log("uploadFile err:%s", err)
})
/**
 * 文件上传
 */
function uploadFiles(host, path, port, fileDataInfo, fileKeyValue) {
var options = {
  hostname: host,
  port: port?port:80,
  path: path,
  method: 'POST'
}
return new Promise(function(resolve, reject) {
  var req = http.request(options, function(res) {
    var data = ''
    res.on("data", function(chunk) {
      data += chunk;
    })
    res.on('end', () => {
      try{
        resolve(data)
      }catch (err){
        reject(err)
      }
    })
  }).on("error", function (err) {
    reject(err)
  })
  var boundaryKey = Math.random().toString(16);
  var enddata = '\r\n----' + boundaryKey + '--';
  var dataLength = 0;
  var dataArr = new Array();
  for (var i = 0; i < fileDataInfo.length; i++) {
    var dataInfo = "\r\n----" + boundaryKey + "\r\n" + "Content-Disposition: form-data; name=\"" + fileDataInfo[i].urlKey + "\"\r\n\r\n" + fileDataInfo[i].urlValue;
    var dataBinary = new Buffer(dataInfo, "utf-8");
    dataLength += dataBinary.length;
    dataArr.push({
      dataInfo: dataInfo
    });
  }
  var files = new Array();
  for (var i = 0; i < fileKeyValue.length; i++) {
    var content = "\r\n----" + boundaryKey + "\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: form-data; name=\"" + fileKeyValue[i].urlKey + "\"; filename=\"" + pathUtil.basename(fileKeyValue[i].urlValue) + "\"\r\n" + "Content-Transfer-Encoding: binary\r\n\r\n";
    var contentBinary = new Buffer(content, 'utf-8');
    files.push({
      contentBinary: contentBinary,
      filePath: fileKeyValue[i].urlValue
    });
  }
  var contentLength = 0;
  for (var i = 0; i < files.length; i++) {
    var filePath = files[i].filePath;
    if (fs.existsSync(filePath)) {
      var stat = fs.statSync(filePath);
      contentLength += stat.size;
    } else {
      contentLength += new Buffer("\r\n", 'utf-8').length;
    }
    contentLength += files[i].contentBinary.length;
  }
  req.setHeader('Content-Type', 'multipart/form-data; boundary=--' + boundaryKey);
  req.setHeader('Content-Length', dataLength + contentLength + Buffer.byteLength(enddata));
  // 将参数发出
  for (var i = 0; i < dataArr.length; i++) {
    req.write(dataArr[i].dataInfo)
    //req.write('\r\n')
  }
  var fileindex = 0;
  var doOneFile = function() {
    req.write(files[fileindex].contentBinary);
    var currentFilePath = files[fileindex].filePath;
    if (fs.existsSync(currentFilePath)) {
      var fileStream = fs.createReadStream(currentFilePath, {bufferSize: 4 * 1024});
      fileStream.pipe(req, {end: false});
      fileStream.on('end', function() {
        fileindex++;
        if (fileindex == files.length) {
          req.end(enddata);
        } else {
          doOneFile();
        }
      });
    } else {
      req.write("\r\n");
      fileindex++;
      if (fileindex == files.length) {
        req.end(enddata);
      } else {
        doOneFile();
      }
    }
  };
  if (fileindex == files.length) {
    req.end(enddata);
  } else {
    doOneFile();
  }
 })
}

二、Node.js后端文件接收及保存

这边用formidable来解析获取文件名和服务端保存的文件路径。

var formidable = require('formidable')
var Promise = require('bluebird')
app.post('/api/upload/file', function (req, res) {
  var form = new formidable.IncomingForm()
  if (options.saveDir) {
    form.uploadDir = options.saveDir
  }
  Promise.promisify(form.parse, form)(req)
    .spread(function(fields, files) {
      return Object.keys(files).map(function(field) {
        var file = files[field]
      //file.name字段获取文件名,file.path字段获取服务端保存的文件路径,这边可以把文件移动存储到你想要的位置
        console.log(">>>>>>>fileName:%s, filePath:%s", file.name, file.path);
      })
    }).catch(function(err) {
     res.status(500)
       .json({
         success: false
        , error: 'ServerError'
       })
     })
});

三、Node.js后端文件下载

//示例调用
donwloadFile("test.cn", "80", "/test/test.zip", "/User/Download/temp/test.zip")

function downloadFile(host, port, path, downloadPath) {
  var writeStream = fs.createWriteStream(downloadPath);
  var option = {
    hostname: host,
    port: port,
    method: 'GET',
    path: path
  }
  var req = http.request(option, function (res) {
    if (res.statusCode == 200) {
      res.pipe(writeStream);
      res.on('data', (chunk) => {
      });
      res.on("end", function () {
      });
    } else {
    }
  });
  req.on("error", function (err) {
  });
  req.end();
}

如果后端服务用的是https,在做文件下载操作的时候端口用80就会报如下异常:

error:write EPROTO 140036986775360:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:
unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:827:

此时端口要改为443,即:

donwloadFile("test.cn", "443", "/test/test.zip", "/User/Download/temp/test.zip")

 

扩展阅读:

Node.js后端zip压缩文件保存和读取

JavaScript前后端JSON使用方法详解

 

转载请注明出处:陈文管的博客 – Node.js后端文件上传、文件接收保存及文件下载实现

 

扫码或搜索:文呓

博客公众号

微信公众号 扫一扫关注

博客公众号
博客公众号

GitHub

https://github.com/wenguan0927

近期文章

  • Android平台动画类型详解
  • Kotlin null 详解
  • Android 残影数字动画实现详解
  • Android 卡片旋转切换动效实现详解
  • Android 心率动画自定义控件实现

友情链接

崔庆才的个人博客

Trinea  (codeKK)

Piasy

Paincker

wanandroid

陈祖杰的BLOG

闽ICP备18001825号-1 · Copyright © 2023 · Powered by chenwenguan.com