-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse-post-data_1.1.js
More file actions
360 lines (327 loc) · 17.9 KB
/
Copy pathparse-post-data_1.1.js
File metadata and controls
360 lines (327 loc) · 17.9 KB
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/**
* 文件名:parse-post-data.js
* 作者:https://github.com/fengfanv
* 描述:post数据解析器
* 修改时间:2021-3-24
* 资源地址:https://github.com/fengfanv/parse-post-data
* parseFormData方法案例(上传文件):https://github.com/fengfanv/JS-library/tree/master/node/YUMA_uploadFiles
* parseFormData方法案例(上传文件夹):https://github.com/fengfanv/JS-library/tree/master/node/YUMA_uploadFolder
* 此版本与github上版本不一致,github上为标准版,这个为缩减优化版,只支持原生node(http,https)
*/
const fs = require('fs');
const qs = require('querystring');
//匹配POST提交数据方式正则
const contentTypeRegExp = {
'application/json': new RegExp('application/json', 'i'),
//编码格式:序列化的json字符串
'application/x-www-form-urlencoded': new RegExp('application/x-www-form-urlencoded', 'i'),
//编码格式:key1=val1&key2=val2
'multipart/form-data': new RegExp('multipart/form-data', 'i'),
//编码格式:可以上传文件等二进制数据,上传表单键值对,最后会转化为一条信息,信息内每条数据已边界字符串为单元,用分隔符分开
'text/plain': new RegExp('text/plain', 'i')
};
//支持http,https
/**
* 解析post数据
* @param {*} request
* @param {any} config 解析配置,如上传文件保存地址
* @param {*} next
*/
exports.parsePost = function (request, config, next) {
return new Promise(function (resolve, reject) {
try {
if (request.method == 'POST') {
var header = request.headers;
var contentType = typeof header['content-type'] != 'undefined' ? header['content-type'] : null;
if (contentTypeRegExp['multipart/form-data'].exec(contentType)) {
//multipart/form-data编码数据,用base64编码方式接受,
request.setEncoding('base64');
};
var postData = '';
request.on('data', function (chunk) {
postData += chunk;//这种方式接收数据有1gb左右的限制
});
request.on('end', async function () {
//console.log(contentType);
if (contentTypeRegExp['application/json'].exec(contentType)) {
let data = null;
if (new RegExp('^{[^]*}$', 'i').test(postData.trim())) {
//json字符串
data = JSON.parse(postData);
} else {
//key1=val1&key2=val2,兼容jquery设置content-type不准确的bug
data = qs.parse(postData);
}
//console.log(postData);
request.body = data;
next && next();
resolve(data);
} else if (contentTypeRegExp['application/x-www-form-urlencoded'].exec(contentType)) {
let data = qs.parse(postData);
request.body = data;
next && next();
resolve(data);
} else if (contentTypeRegExp['multipart/form-data'].exec(contentType)) {
let storageFilePath = typeof config != 'undefined' && typeof config.storageFilePath != 'undefined' ? config.storageFilePath : undefined;
let data = await parseFormData(postData,storageFilePath);//解析formdata数据
let newData = {};
for (let key in data) {
newData[key] = data[key].data;
}
//console.log(newData);
request.body = newData;
next && next();
resolve(newData);
} else {
//其它方式编码数据不处理直接返回
next && next();
resolve(postData);
}
});
request.on('error', function (err) {
reject(err);
});
request.on('close', function (message) {
reject(message);
});
} else {
next && next();
resolve();
}
} catch (err) {
console.log('parse-post-data', err);
reject(err);
}
})
};
//解析formdata数据
function parseFormData(formdata, storageFilePath) {
if (typeof storageFilePath == 'undefined') {
storageFilePath = __dirname;
}
return new Promise(function (resolve, reject) {
//2020年5月6日通过接收编码为base64的数据在转换成binary编码数据实现文件上传
var postData = Buffer.from(formdata, 'base64').toString('binary');//将编码为base64的字符串转换为buffer实体再转换为编码为binary的字符串
var postData_utf8 = Buffer.from(formdata, 'base64').toString();
//.replace(/^\s\s*/, '')匹配空格
//1、找到处理后生成的hexname值,已便定位formdata处理过的文件数据和其它参数在那个位置
//hexname值作用,用来标记数据位置(这个其实叫边界字符串)
//找上面那个hexname值技巧,从尾部找找到\r\n停止
var postDataLen = postData.length;
var i = postDataLen;
var foundValue = '';
//console.log('开始找hexname值');
while (foundValue !== '\r\n--') {
//console.log(foundValue);
//console.log(i);
i--;
foundValue = postData.substr(i, 4);
}
var hexname = postData.substring(i, postDataLen).replace(/[\r\n]/g, "");
hexname = hexname.substring(0, hexname.length - 2);
//console.log(hexname);
//至此hexname输出的后16为,为hex值
//2、根据上面找的hexname值把每一个formdata中每个参数切割,分出来
//处理编码为binary
var Arr1 = postData.split(hexname + '\r\n');
var dataArr = Arr1.slice(1, Arr1.length);
//处理编码为utf8
var Arr1_utf8 = postData_utf8.split(hexname + '\r\n');
var dataArr_utf8 = Arr1_utf8.slice(1, Arr1_utf8.length);
//通过hexname切割并把formdata中的参数转到数组后,在过滤一下,防止在不上传文件时,防止切割后的参数体中还有hexname的情况
dataArr.forEach(function (item, index) {
//处理编码为binary
dataArr[index] = item.replace(hexname, "");
//处理编码为utf8
dataArr_utf8[index] = dataArr_utf8[index].replace(hexname, "");
});
var dataArrLen = dataArr.length;
//3、分析数据,把formdata中每一个参数的数据状态信息(数据名称,数据类型什么的)和数据本体提取出来
var dataBody = {}; //dataBody用来存放处理后formdata中每一个参数状态信息和数据体的容器
console.log('FormData 参数数量:' + dataArrLen);
function eachArr(index) {
//item现在是formdata中某一个参数的数据状态信息(数据名称,数据类型什么的)和数据值本身的结合体
let item = dataArr[index];
let addIndex = index + 1;
//处理编码为binary
let valueData = item;
//处理编码为utf8
let valueData_utf8 = dataArr_utf8[index];
//寻找formdata中某一个参数的数据状态信息(数据名称,数据类型什么的)和数据本体中间的位置
let endzuobiao = valueData.search('\r\n\r\n');
//3.1、根据endzuobiao坐标之前为数据状态信息(数据名称,数据类型什么的)(3.1、提取参数的数据状态信息)
//console.log(valueData.substring(0, endzuobiao));
//把参数的数据状态信息字符串转换成arr,因为一个参数的状态信息不光有数据名称,还有别的数据说明属性,所以要把它换成arr
//处理编码为binary
let paramArr = valueData.substring(0, endzuobiao).replace(/[\r\n]/g, ";").split(';');
//处理编码为utf8
let paramArr_utf8 = valueData_utf8.substring(0, endzuobiao).replace(/[\r\n]/g, ";").split(';');
//把参数的数据状态的信息提分别提取到对象中
//提取参数 数据状态信息用utf8编码的,这样可以防止上传文件时,如果文件名称是中文的话无法识别中文的问题
let param = {};//用于临时存放某个已处理好参数(已处理好的数据状态信息和对象和数据体)的容器
paramArr.forEach(function (paramArrItem, paramArrIndex) {
if (paramArrItem.length > 0) {
var fuhao = ":";
if (paramArrItem.search(":") !== -1) {
fuhao = ":";
} else {
fuhao = "=";
}
//处理编码为binary
// var paramArrItemkey = paramArrItem.split(fuhao)[0].replace(/\ +/g, "").replace(/['"]/g, "");
// var paramArrItemvalue = paramArrItem.split(fuhao)[1].replace(/\ +/g, "").replace(/['"]/g, "");
//处理编码为utf8
var paramArrItemkey = paramArr_utf8[paramArrIndex].split(fuhao)[0].replace(/\ +/g, "").replace(/['"]/g, "");
var paramArrItemvalue = paramArr_utf8[paramArrIndex].split(fuhao)[1].replace(/\ +/g, "").replace(/['"]/g, "");
param[paramArrItemkey] = paramArrItemvalue;
}
});
//3.2、根据endzuobiao坐标之后为数据本体(3.2、提取参数的数据本体)
//根据提取的数据状态信息,分别用不同编码方式的数据来提取数据体
if (typeof param['Content-Type'] !== 'undefined') {
//提取文件类数据体,文件类数据用binary编码的,防止数据写入文件后,文件无法使用
//处理编码为binary
let dataBody = valueData.substring(endzuobiao);
let data = dataBody.substring(4, dataBody.length - 4); //去除数据头尾的换行符
//console.log('这应该是图片类数据');
/*
.png 正常解析保存
.jpg 正常解析保存
.mp4 正常解析保存
.mp3 正常解析保存
.txt 正常解析保存,txt内容有中文也会正常保存,
.word 正常保存,但是打开时说无法正常读取文件,需要修复,修复后可正常打开
*/
//把图片类数据写入文件
//跟据config设置文件名称及文件存放地址
let primaryFileName = param.filename.replace(/['"]/g, "");//原文件名
//storageFilePath 存放文件的地址
//2020-8-22日添加,整合上传文件及上传文件夹案例
var filename = param.filename.replace(/['"]/g, "");
//检查是否有文件夹
if (new RegExp('/+').test(filename)) {
//有文件夹
//跟据用户config设置文件存放地址
let filePath = storageFilePath + '/' + filename;
//1、分析文件地址
//上传的文件夹里面的文件经过处理后是的文件地址为:lpr/images/picture.jpg
var pathArr = filename.split('/');
var pathArrLen = pathArr.length;
if (pathArrLen >= 2) {
//有文件夹,检测文件夹是否存在,没有则创建
function ss(ssNumber) {
if (ssNumber === pathArrLen - 1) {
//写入文件
let writerStream = fs.createWriteStream(filePath);
writerStream.write(data, 'binary'); // 标记文件末尾
writerStream.end();
writerStream.on('finish', function () {
console.log('写入完成_' + filename);
param.data = filename;
//执行回调
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
writerStream.on('error', function (err) {
console.log('写入失败_' + filename);
param.errMessage = '写入文件失败!';
//console.log(err.stack);
//执行回调
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
return false;
}
//检测文件夹是否存在
let sz = ssNumber + 1;
let testAddress = storageFilePath + '/' + pathArr.slice(0, sz).join('/');
fs.stat(testAddress, function (pathErr) {
if (pathErr) {
//没有文件夹则创建
fs.mkdir(testAddress, function (createDirErr) {
if (createDirErr) {
console.log('创建文件夹错误_' + testAddress);
console.log(createDirErr);
return false;
}
ss(sz);
});
return false;
}
//有文件夹,不用创建,直接检测下一个
ss(sz);
});
}
ss(0);
} else {
//没有文件夹,直接保存
let writerStream = fs.createWriteStream(filePath);
writerStream.write(data, 'binary'); // 标记文件末尾
writerStream.end();
writerStream.on('finish', function () {
console.log('写入完成_' + filename);
param.data = filename;
//执行回调
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
writerStream.on('error', function (err) {
console.log('写入失败_' + filename);
param.errMessage = '写入文件失败!';
//console.log(err.stack);
//执行回调
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
}
} else {
//没有文件夹
//创建可写为流文件
let filePath = storageFilePath + '/' + filename;
let writerStream = fs.createWriteStream(filePath);
writerStream.write(data, 'binary');
// 标记文件末尾
writerStream.end();
// 处理流事件 ==> finish 事件
writerStream.on('finish', function () {
//finish所有数据已被写入到底层系统时触发。
console.log(param.filename + '_写入完成');
param.data = filePath; //图片类的文件写入保存的文件地址
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
writerStream.on('error', function (err) {
console.log(param.filename + '_写入失败');
//console.log(err.stack);
param.errMessage = '写入文件失败!';
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
});
}
} else {
//提取普通数据体,普通数据用utf8编码的,防止出现中文字无法识别
//处理编码为utf8
let dataBody_utf8 = valueData_utf8.substring(endzuobiao);
let data_utf8 = dataBody_utf8.replace(/[\r\n]/g, "").replace(/--$/, ""); //除去空格和结尾的--
//普通数据,字符串型数据,啥的,object型数据会在浏览器上formdata解析成[object object]所以obj在传输前要转换成字符串
//处理编码为utf8,直接将数据存入
param.data = data_utf8; //将处理好的普通数据体写入到param中
if (addIndex !== dataArrLen) {
eachArr(addIndex);
}
}
dataBody[param.name] = param; //将这个整理好的formdata参数存入到dataBody中
if (index === dataArrLen - 1) {
//formData中所有参数处理完成
resolve(dataBody);
};
};
eachArr(0);
});
}