使用NodeJS实现文件上传、转码、预览功能(零前端)

功能描述:
1. 用户将保存图片的文件夹进行压缩,通过网页上传;
2. 后端对接收到的压缩文件夹进行解压,并将图片转为web格式,存放至服务器指定位置;同时,把web图片列表的路径保存到image.json文件中;
3. 前端页面请求图片的路径时,后端则读取image.json文件的内容,将其返回,前端则只接收图片地址,显示出来即可。

实现过程:文件上传路径配置文件注:之所以将路径作为配置文件来写,是为了方便调试,例如,开发过程中服务器在本地,则解压之后的图片临时存放在本地,正式上线,图片路径则是服务器上指定的路径。
production.json

"upload": {    
	"host": "http://10.xxx.xxx.xx",
	"tempPath": "src/__bak__/upload", // 临时地址    
	"outputPath": "/work/nginx/www/upload",  // 解压、转码之后图片存放的地址    
	"accessFile": "/work/nginx/www/upload/images.json"  // 保存图片路径的image.json文件的地址
}

testing.json

"upload": {    
	"host": "http://127.0.0.1",
    "tempPath": "src/__bak__/upload",
    "outputPath": "src/__bak__/images",
    "accessFile": "src/__bak__/images/images.json"
}

后端共计三个接口

/**
* @api {GET} /upload   请求图片上传页面
* @apiDescription      请求图片上传页面
* @apiVersion 1.0.0
* @apiName uploadHTML
* @apiGroup upload
*
*/
router.get('/upload', upload_ctrl.getUploadHTML);

/**
* @api {POST} /upload   上传图片压缩文件
* @apiDescription       上传图片压缩文件
* @apiVersion 1.0.0
* @apiName uploadFile
* @apiGroup upload
*
* @apiParam (body) {files}  path
*/
router.post('/upload', upload_ctrl.uploadFile);

/**
* @api {GET} /upload/img_urls   请求图片访问地址
* @apiDescription               请求图片访问地址
* @apiVersion 1.0.0
* @apiName getImgUrls
* @apiGroup upload
*
*/
router.get('/upload/img_urls', upload_ctrl.getImgUrls);

读取文件路径

const upload_conf = config.get<{ host: string; tempPath: string; outputPath: string; accessFile: string; }>('upload');
const url_conf = config.get<{ thumbor: { hostUrl: string; } }>('upstream');

第一步:用户访问上传页面 (http://127.0.0.1:8099/upload ),后端读取upload.html, 将其内容返回

async function getUploadHTML(ctx: RouterContext) {
  const fileName = "./src/scm-board/router/file-upload/upload.html";
  const res = fs.readFileSync(fileName);
  ctx.res.setHeader('Content-Type', 'text/html;charset="utf-8');
  ctx.body = res;
  return;
}

第二步:用户选择压缩后的文件夹进行上传,后端将其保存到临时路径,然后读取,将文件夹解压、图片进行转码,并生成image.json,将转码后的图片路径放入image.json文件中

async function uploadFile(ctx: RouterContext) {
  const files: Files = ctx.request.files;
  if (!(files && files.image)) {
    ctx.body = HoResponseUtils.error<any>({
      error_no: 100301
    });
    return;
  }
  if (files.image && files.image['length']) {
    ctx.body = HoResponseUtils.error<any>({
      error_no: 100302
    });
    return;
  }
  ca_logger.info(upload_conf);
  try {
    // 另存为本地临时文件
    const tmpPath = await _saveFile(files);
    // 解压
    const originPath = upload_conf.outputPath + '/origin';
    const imgPathListStr = await _decompression(tmpPath, originPath);
    // 转码
    const thumbPath = upload_conf.outputPath + '/thumb';
    await _thumbImgList(imgPathListStr, thumbPath);
  } catch (error) {
    ctx.body = HoResponseUtils.error<any>({
      error_no: 100302,
      error_message: error
    });
    return;
  }
  ctx.body = HoResponseUtils.normal<any>({
    data: '上传成功'
  });
  return;
}

// 重命名为image,并另存为本地临时文件下
function _saveFile(files): Promise<string> {
  const file = files.image as File;
  const suffix = file.name.split('.').pop();
  const tmpPath = `${upload_conf.tempPath}/images.${suffix}`;
  return new Promise((resolve, reject) => {
    try {
      caUtils.mkdir(tmpPath);
      const data = fs.readFileSync(file.path);
      fs.writeFileSync(tmpPath, data)
      resolve(tmpPath);
    } catch (err) {
      reject('文件读取失败,请上传正确的压缩文件夹!')
    }
  });
}


/**
* @param tempPath
* @param outputPath
* 读取 tempPath 路径下的压缩文件 解压之后,解压后的文件放至 outputPath
* 解压之后临时文件可删可不删,每次上传都会重命名为image,第二次上传,会覆盖掉第一次上传的压缩文件
*/
function _decompression(tempPath: string, outputPath: string): Promise<string> {
  const outputArr = [];
  const myStream = extractFull(tempPath, outputPath, {
    $bin: pathTo7zip,
    $progress: true
  });
  return new Promise((res, rej) => {
    myStream.on('data', function (data) {
      outputArr.push(upload_conf.host + '/upload/origin/' + data.file)
    });
    myStream.on('end', function () {
      ca_logger.info('Complete!');
      if (outputArr.length) {
        res(outputArr.join(','));
      } else {
        rej('文件夹为空,请上传正确的压缩文件夹!');
      }
    });
    myStream.on('error', (err) => {
      ca_logger.error('Error:', err)
      rej('文件解压失败,请上传正确的压缩文件夹!');
    });
  });
}

第三步:前端请求后端(http://127.0.0.1:8099/upload/img_urls),后端读取image.json文件并将其返回,前端则得到转码后的图片路径

async function getImgUrls(ctx: RouterContext) {
  const filePath = upload_conf.accessFile;
  if (!fs.existsSync(filePath)) {
    ctx.body = HoResponseUtils.error<any>({
      error_no: 100304
    });
    return;
  }
  try {
    const res = fs.readFileSync(filePath);
    const result = JSON.parse(res.toString())
    ctx.body = HoResponseUtils.normal<any>({
      data: result
    });
    return;
  } catch (e) {
    ctx.body = HoResponseUtils.error<any>({
      error_no: 100305
    });
    return;
  }
}

// 格式如下:
// {
//   "code": 0,
//   "message": null,
//  "data": {
//     "path":"/work/nginx/www/upload/11.jpg,/work/nginx/www/upload/12.jpg"
//   }
// }
// 前端则将host+path即可显示图片

源码:https://github.com/zhanghongyublog/node.js/tree/master/file-upload

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值