内容简介:处理文件上传数据,也是前后端交互中重要的功能,它的处理方式与数据不同。接下来,通过一个例子查看服务端接收到的文件上传数据。首先,在post_file.html中,新建一个用与上传文件的表单:
处理文件上传数据,也是前后端交互中重要的功能,它的处理方式与数据不同。
接下来,通过一个例子查看服务端接收到的文件上传数据。
首先,在post_file.html中,新建一个用与上传文件的表单:
form的属性enctype="multipart/form-data"代表表单上传的是文件。
enctype的默认值为enctype="application/x-www-form-urlencoded"表示上传的是数据类型,此时服务端接收到的数据为“username=lee&password=123456&file=upload.txt”。
代码示例:/lesson16/post_file.html
<form action="http://localhost:8080/upload" method="POST" enctype="multipart/form-data"> 用户:<input type="text" name="username" value="lee"><br/> 密码:<input type="password" name="password" value="123456"><br/> <input type="file" name="file" id=""><br/> <input type="submit" value="提交"> </form> 复制代码
其次,在server.js中,查看接收到的表单提交数据:
代码示例:/lesson16/server.js
const http = require('http') const server = http.createServer((req, res) => { let arr = [] req.on('data', (buffer) => { arr.push(buffer) }) req.on('end', () => { let buffer = Buffer.concat(arr) console.log(buffer.toString()) }) }) server.listen(8080) 复制代码
最后,在表单中上传/lesson16/upload.txt文件,并查看打印出的结果:
------WebKitFormBoundaryL5AGcit70yhKB92Y Content-Disposition: form-data; name="username" lee ------WebKitFormBoundaryL5AGcit70yhKB92Y Content-Disposition: form-data; name="password" 123456 Content-Disposition: form-data; name="file"; filename="upload.txt" Content-Type: text/plain upload ------WebKitFormBoundaryL5AGcit70yhKB92Y-- 复制代码
文件上传数据分析
通过分析上面这个例子中,服务端接收到的数据,可以得到以下信息:
const boundary = '--' + req.headers['content-type'].split('; ')[1].split('=')[1]
由此可以看出,文件上传数据虽然有些乱,但还是有规律的,那么处理思路就是按照规律,将数据切割之后,取出其中有用的部分。
文件上传数据简化
先回顾一下上面的数据,并将回车符标记出来:
------WebKitFormBoundaryL5AGcit70yhKB92Y\r\n Content-Disposition: form-data; name="username"\r\n \r\n lee\r\n ------WebKitFormBoundaryL5AGcit70yhKB92Y\r\n Content-Disposition: form-data; name="password"\r\n \r\n 123456\r\n Content-Disposition: form-data; name="file"; filename="upload.txt"\r\n Content-Type: text/plain\r\n \r\n upload\r\n ------WebKitFormBoundaryL5AGcit70yhKB92Y-- 复制代码
可以看出,每段数据的结构其实是这样的:
------WebKitFormBoundaryL5AGcit70yhKB92Y\r\nContent-Disposition: form-data; name="username"\r\n\r\nlee\r\n 复制代码
将每段上传数据简化如下:
<分隔符>\r\n字段头\r\n\r\n内容\r\n 复制代码
也就是说,整个表单的数据,就是按照这样的数据格式组装而成。
需要注意的是,在表单数据的结尾不再是\r\n,而是“--”。
文件上传数据处理步骤
- 用<分隔符>切分数据:
[ ‘’, "\r\n字段信息\r\n\r\n内容\r\n", "\r\n字段信息\r\n\r\n内容\r\n", "\r\n字段信息\r\n\r\n内容\r\n", '--' ] 复制代码
- 删除数组头尾数据:
[ "\r\n字段信息\r\n\r\n内容\r\n", "\r\n字段信息\r\n\r\n内容\r\n", "\r\n字段信息\r\n\r\n内容\r\n", ] 复制代码
- 将每一项数据头尾的的\r\n删除:
[ "字段信息\r\n\r\n内容", "字段信息\r\n\r\n内容", "字段信息\r\n\r\n内容", ] 复制代码
- 将每一项数据中间的\r\n\r\n删除,得到最终结果:
[ "字段信息", "内容", "字段信息", "内容", "字段信息", "内容", ] 复制代码
Buffer的数据处理
由于文件都是二进制数据,不能直接将其转换为字符串后再进行处理,否则数据会出错,因此要通过Buffer模块进行数据处理操作。
Buffer模块提供了indexOf方法获取Buffer数据中,其参数所在位置的index值。
Buffer模块提供了slice方法,可通过index值切分Buffer数据。
先测试一下这两个方法:
示例代码:/lesson16/buffer.js
let buffer = Buffer.from('lee\r\nchen\r\ntest') const index = buffer.indexOf('\r\n') console.log(index) console.log(buffer.slice(0, index).toString()) 复制代码
可以看到打印结果分别为3和"lee",也就是说,我们先找到了"\r\n"所在的index为3,之后从Buffer数据的index为0的位置,切割到index为3的位置,得到了正确的结果。
由此,可以封装一个专门用于切割Buffer数据的方法:
示例代码:/lesson16/bufferSplit.js
module.exports = function bufferSplit(buffer, separator) { let result = []; let index = 0; while ((index = buffer.indexOf(separator)) != -1) { result.push(buffer.slice(0, index)); buffer = buffer.slice(index + separator.length); } result.push(buffer); return result; } 复制代码
有了bufferSplit方法,就可以正式开始处理数据了。
文件上传数据处理
根据上面的思路,就可以实现一个完整的文件上传流程。
代码示例:/lesson16/server.js
const http = require('http') const fs = require('fs') const bufferSplit = require('./bufferSplit') const server = http.createServer((req, res) => { const boundary = `--${req.headers['content-type'].split('; ')[1].split('=')[1]}` // 获取分隔符 let arr = [] req.on('data', (buffer) => { arr.push(buffer) }) req.on('end', () => { const buffer = Buffer.concat(arr) console.log(buffer.toString()) // 1. 用<分隔符>切分数据 let result = bufferSplit(buffer, boundary) console.log(result.map(item => item.toString())) // 2. 删除数组头尾数据 result.pop() result.shift() console.log(result.map(item => item.toString())) // 3. 将每一项数据头尾的的\r\n删除 result = result.map(item => item.slice(2, item.length - 2)) console.log(result.map(item => item.toString())) // 4. 将每一项数据中间的\r\n\r\n删除,得到最终结果 result.forEach(item => { console.log(bufferSplit(item, '\r\n\r\n').map(item => item.toString())) let [info, data] = bufferSplit(item, '\r\n\r\n') // 数据中含有文件信息,保持为Buffer类型 info = info.toString() // info为字段信息,这是字符串类型数据,直接转换成字符串,若为文件信息,则数据中含有一个回车符\r\n,可以据此判断数据为文件还是为普通数据。 if (info.indexOf('\r\n') >= 0) { // 若为文件信息,则将Buffer转为文件保存 // 获取字段名 let infoResult = info.split('\r\n')[0].split('; ') let name = infoResult[1].split('=')[1] name = name.substring(1, name.length - 1) // 获取文件名 let filename = infoResult[2].split('=')[1] filename = filename.substring(1, filename.length - 1) console.log(name) console.log(filename) // 将文件存储到服务器 fs.writeFile(`./upload/${filename}`, data, err => { if (err) { console.log(err) } else { console.log('文件上传成功') } }) } else { // 若为数据,则直接获取字段名称和值 let name = info.split('; ')[1].split('=')[1] name = name.substring(1, name.length - 1) const value = data.toString() console.log(name, value) } }) }) }) server.listen(8080) 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web标准和SEO应用实践
Aarron Walter / 李清 / 机械工业出版社 / 2008 / 36.00元
本书是关于搜索引擎优化和易发现性的技术指南。. 本书介绍Web标准、可访问性以及Ajax、API、Flash和微格式等内容,包括标记策略、服务器端策略、内容策略、建构易发现的博客、在网站内添加搜索、防止易发现性障碍、用邮件列表挽回流量、将易发现性付诸实践。 本书适合网站开发者与SEO技术业余爱好者等参考。 这不是为营销专家写的一本SEO的书。 针对那些想要找到网站的目标用户......一起来看看 《Web标准和SEO应用实践》 这本书的介绍吧!