使用AJAX和JSON是现在和服务器进行通信的标准形式,但是直接这样对file类型的input的内容进行上传是无法成功的。因为对文件进行序列化后得到的是一个空对象。

1
<input type="file" id="file-uploader" />
1
2
3
4
5
6
7
8
const fileUploader = document.querySelector('#file-uploader');
const fileHandleUrl = '/example/file-upload';

fileUploader.addEventListener('change', () => {
  const file = this.files[0];

  console.log(JSON.stringify(file));    // => "{}"
});

这显然不是我们想要的。要如何才能上传成功呢?很简单,使用FormData接口就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const fileUploader = document.querySelector('#file-uploader');
const fileHandleUrl = '/example/file-upload';

fileUploader.addEventListener('change', function() {
  const file = this.files[0];
  const form = new FormData();

  form.append('Content-Type', 'multipart/form-data');
  form.append('file', file);

  const xhr = new XMLHttpRequest();

  xhr.onload = () => {
    // 文件上传成功逻辑
  };

  xhr.onerror = () => {
    // 文件上传出错的逻辑
  };

  xhr.open('POST', fileHandleUrl);
  xhr.send(form);

  this.value = '';
});

示例中直接使用的XMLHttpRequest实现AJAX,实际生产中,使用superagent或者其他模块更好。

服务器端处理也很简单,这里以express为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'use strict';

const path = require('path');

const express = require('express');
const formidable = require('formidable');

const app = express();

app.use(express.static(path.join(__dirname, 'public')));

app.post('/example/file-upload', (req, res, next) => {
  const form = new formidable.IncomingForm();

  form.parse(req, function(err, fields, files) {
    // 文件在files对象中
    // 处理上传的文件的逻辑
    res.send('success');
  });
});

app.listen(3000, () => {
  console.log('http://localhost:3000');
});

因为使用类型为multipart/form-data的表单上传,所有需要使用第三方模块处理类型为multipart/form-data的数据,这里使用的是formidable,还有其他选择,使用自己认为最适合的就好。

参考

Using FormData Objects

Express 4 FormData multipart parse POST request