图片上传
最近给设计妹妹搞了模板上传功能,里面涉及到了图片上传功能。
input
在前端涉及到图片上传,都会用到input标签,监听onchange事件,可以拿到file文件
1 | <input type="file" accept=".png" class="input_file" /> |
1 | let file = document.getElementsByClassName('input_file')[0]; |
利用formData可以把二进制数据传输给后端。
fetch
由于是内部项目直接使用fetch
1 | var ajaxImg = (url, params = {}) => { |
封装一个请求,单独传输图片数据。 在之前onchange里面调用ajaxImg将formData数据发送给后端。
fetch数据异常
后端一直没有拿到图片的数据,换成jquery的ajax就可以。
对比network,发现fetch的request content-type 里面内容少了 boundary=XXXXXXX
后来发现这个是浏览器的BUG,解决方案就是在请求的时候不需要加content-type。
这样前端的基本任务就完成了。
node
后端使用express来构建服务,解析form-data数据,要用到multer库
代码简单如下
1 | var fs = require("fs"); |
multer
借助multer能很快的处理图片问题,但是它是怎么处理的呢?
dicer is A very fast streaming multipart parser for node.js.
所以dicer是其核心,我们尝试直接使用dicer来处理图片。
dicer
稍微改改官方的例子
1 | var inspect = require('util').inspect, |
fs.writeFileSync('./test1.png', data);
直接在请求数据回调里面保存为图片,发现是能够成功的。
在这个例子里完全没有express了,那我们尝试下自己来处理下图片数据。
原生处理
1 | const fs = require('fs'); |
核心代码如上,这样可以拿到formdata里面所有的数据,可以查看其数据。
可以发现其数据是通过请求头里面的boundary值进行分割的。
所以先简单取出boundary值
1 | let type = req.headers['content-type']; |
然后进行数据切割1
2strs = strs.split('--'+boundary + '--')[0];
let forms = strs.split('--'+boundary + '\r\n').filter(val=> val);
每块内容区域根据\r\n\r\n
和\r\n
进行切割,可以将Content-Disposition、Content-Type, 内容区分开来。
完整代码可以尝试如下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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87const fs = require('fs');
const http = require('http');
const port = 8013;
http.createServer((req, res)=> {
let url = req.url;
let statics = 'html#png#js';
if(url === '/') {
url = '/index.html';
}
if(url.indexOf('.')>-1) {
let name = url.replace(/.*\./gim, '');
if(statics.indexOf(name) > -1) {
res.write(fs.readFileSync(`./public/${url}`));
res.end();
}
}
if(url.indexOf('/query')>-1) {
res.writeHead(200, {
'Content-Type': 'application/json;charset=utf-8;'
});
res.write(JSON.stringify({
"216*216": "./img/216*216.png"
}));
res.end();
}
if(url.indexOf('/upload')>-1) {
let strs = '';
let type = req.headers['content-type'];
let boundary = type.split('boundary=')[1];
req.on('data', (str)=> {
strs += str.toString('binary');
})
req.on('end', ()=> {
fs.writeFileSync('./text', buffer.toString());
res.writeHead(200, {
'Content-Type': 'application/json;charset=utf-8;'
});
strs = strs.split('--'+boundary + '--')[0];
let forms = strs.split('--'+boundary + '\r\n').filter(val=> val);
let formData = [];
forms.forEach(val=> {
let vals = val.split('\r\n\r\n');
let value = vals[1];
let vlass = vals[0].split('\r\n').filter(val=> val);
let name = vlass[0].match(/name="([\w]{0,})"/)[1];
let type;
if(vlass[1] && vlass[1].indexOf('Content-Type') > -1) {
type = vlass[1].match(/Content-Type: (.*)/)[1];
fs.writeFileSync('./test1.png', new Buffer(value , 'binary'));
}else {
type = 'text/plain';
value = value.split('\r\n')[0];
}
formData.push({
type,
name,
value
})
})
console.log(formData);
res.write(JSON.stringify({
"success": true
}));
res.end();
})
}
}).listen(port);
这里会发现这么使用1
2
3req.on('data', (str)=> {
strs += str.toString('binary');
})
这个原因在于request里面使用的是buffer数据结构,如果像字符串操作的话,不管咋样都无法保存图片的。
而dicer是直接对buffer数据进行处理的。
总结
通过这一波的折腾算是对整个图片上传流程有了了解。当然在正式项目里面还是建议大家去用已经封装好的模块,能够避免很多问题。