简单的node文件上传下载及中文乱码问题解决

1. 基于MEAN的技术栈,使用restful风格的接口

2. 在前端代码中放置文件上传按钮和处理表单数据

<div class="upload-file btn btn-sm btn-primary mb-2">
  <span><i class="fa fa-upload">i> Uploadspan>
  <input type="file" class="btn btn-sm btn-primary mb-2" (change)="fileChange($event, topic)" placeholder="Upload file" accept=".csv,.xls">
div>

处理上传文件,生成表单数据

fileChange(event, topic) {
  this.topic = topic;
  const fileList: FileList = event.target.files;

  const file: File = fileList[0];

  const formData: FormData = new FormData();
  formData.append('_id', topic._id);
  formData.append('file', file, file.name);

  this.topicService.uploadMark(formData).subscribe((res) => {
    this.topic.marks = res;
    this.toast.setMessage('item import successfully.', 'success');
  }, error => console.log(error));
}
 
  
uploadMark(fileData: FormData): Observable<any> {
  return this.http.post('/api/upload', fileData);
}

3. 后端接收上传文件

文件上传的router

export default function setRoutes(app) {

  const router = express.Router();
// file uplaod
router.route('/upload').post(uploadCtrl.uploadFile);
// Apply the routes to our application with the prefix /api
app.use('/api', router);
}

在路由中,req的file字段是获取不到上传文件的,或许可以通过设置bodyParser来处理,但我这里使用一个比较常见的库multer。

npm install multer --save

import * as path from 'path';
import * as multer from 'multer';
import TopicService from '../services/topic';

export default class UploadCtrl {
  uploadFile = (req, res) => {
    const topicService = new TopicService();

    // 获取上传文件
    const uploading = multer({
      dest: path.join(__dirname, '../public/uploads'),
    }).single('file'); // 这里的file是formData.append('file', file, file.name)里的名称
    uploading(req, res, (err) => {
      if (err) {
        return console.error(err);
      }

      const topicId = req.body._id;
      const uploadFile = req.file;

      // 保存数据
      const save = async () => {
        const markList = await topicService.parseMark(uploadFile.path);
        const db = await topicService.saveDB(topicId, markList);
        return {
          markList: markList,
          db: db,
        };
      };

      save().then((result) => {
        res.status(200).json(result.markList);
      }, error => {
        console.error(error);
      });
    });
  }
}

4. 处理上传文件的乱码

上传的文件是一个中文的csv,解析时出现了乱码,使用iconv-lite进行转换

npm install iconv-lite --save

import * as iconv from 'iconv-lite';
import * as Buffer from 'bufferhelper';

export default class IconvHelper {
  /**
   * 用于文件上传的转码
   * @param fileStr
   * @returns {string}
   */
  static iconv2utf8 = (fileStr) => {
    return iconv.decode(fileStr, 'gbk');
  }

  /**
   * 用于文件下载的转码
   * @param fileStr
   * @returns {NodeBuffer}
   */
  static iconv2gbk = (fileStr) => {
    return iconv.encode(fileStr, 'gbk');
  }
}

bufferhelper是一个buffer的增强类,但这里使用后并不能正确赋值,所以这里暂且没有使用

对csv文件进行解析,生成数组,下一步可以保存到数据库

parseMark = (filePath) => {
  return new Promise((resolve, reject) => {
    // 读取文件内容
    fs.readFile(filePath, (error, data) => {
      if (error) {
        return reject(error);
      }

      const text = IconvHelper.iconv2utf8(data);

      const markList = [];
      // 将文件按行拆成数组
      text.split(/\r?\n/).forEach((line, index) => {
        const arr = line.split(',');
        if (index > 0 && arr[0]) {
          markList.push({
            userId: arr[0],
            username: arr[1],
            donePageCount: arr[2],
            areaCount: arr[4],
            name: arr[6],
          });
        }
      });
      resolve(markList);
    });
  });
}

5. 下载文件

res.setHeader('Content-disposition', `attachment; filename='${result.name}-member.csv'`);
res.setHeader('Content-type', 'text/csv; charset=GBK');
res.end(IconvHelper.iconv2gbk(content));

6. 处理下载文件的乱码

由于node.js只支持'ascii', 'utf8', 'base64', 'binary'的编码方式,不支持MS的utf8 + BOM格式,网上有说增加BOM头,如下示:

const msExcelBuffer = Buffer.concat([
  new Buffer('\xEF\xBB\xBF', 'binary'),
  new Buffer(IconvHelper.iconv2gbk(content))
]);

但实际并没有起作用,最后只是简单的encode成gbk问题得到解决

res.end(IconvHelper.iconv2gbk(content));

你可能感兴趣的:(js)