前言
🫥需求:这个需要上传一个文件,后端你支持一下吧!听到这句话心里很自然的反应就是,服务端逻辑关我前端什么事,醒一醒,你现在是服务端开发了,快用Node把基本的功能开发下试试看,我们知道文件上传的功能对于前端来说就是一个基本的formadata
进行表单提交的需求,很容易就实现了,但是对于服务端来讲就不仅仅是这个样子了,无论是什么技术栈,在进行文件上传的功能开发的时候大部分时候都是使用插件,但是既然我们刚学习完HTTP模块,那么就是使用这个模块来手写一个简单的demo
版本的文件上传吧,既能巩固下HTTP模块的使用,还能理解文件上传的底层处理方式。
一.文件上传的错误❌方式
👹首先我们按照我们正常的思维方式来搭建一个HTTP服务,然后获取客户端的数据打印,看是否能够接受到客户端上传的二进制的buffer数据,然后创建可写流来进行写入。
js
const http = require("http")
const fs = require("fs")
const server = http.createServer((req, res) => {
// 客户端上传的数据是表单数据
const writeStream = fs.createWriteStream('./picture.png', {
flags: 'a+'
})
req.on('data', (data) => {
writeStream.write(data)
})
req.on('end', () => {
res.end("文件上传成功")
})
})
server.listen(8000, () => {
console.log("服务启动在8000端口")
})
🫥我们通过apifox
来模拟客户端发送一个请求,来上传一张图片
🤡点击请求,我们可以看到服务端终端打印文件上传成功的字样。
🥺然后我们就会看到成功生成了一张图片,到那时当我们点击它的时候这个图片是打不开的
错误内容如下:
🤡没错,这样的上传方式是错误的,但是上传的整体流程是正确的,文件上传的整体流程是:
- 客户端通过将
input
的类型设置为file
来上传文件,上传的方式是通过formdata
的方式。 - 服务端搭建一个服务,获取客户端发送的请求,拿到对应的二进制
buffer
内容。 - 然后将这个二进制buffer的内容创建可写流进行写入,生成一张一模一样的图片。
- 然后触发结束回调,显示文件上传成功。
🥱那么错误究竟在什么地方哪?先别急,我们既然是学习就慢慢来,我们先对上述错误的代码进行优化,其实req
对象是一个可读流,既然是读取
--->写入
那么我们就可以使用pip
(见Node-Stream)的方式直接一把梭哈。
js
const http = require("http")
const fs = require("fs")
const server = http.createServer((req, res) => {
// 客户端上传的数据是表单数据
const writeStream = fs.createWriteStream('./picture.png', {
flags: 'a+'
})
req.pipe(writeStream) // 直接通过管道进行写入
})
server.listen(8000, () => {
console.log("服务启动在8000端口")
})
🦝接下来我们来排查错误,首先我们直接查看buffer
是看不懂的,因为buffer全部是基于binary
展示的进制,为了能够看懂我们使用req.setEncoding('binary')
来设置一下编码方式,更改之后的代码如下
js
const http = require("http")
const fs = require("fs")
const server = http.createServer((req, res) => {
req.setEncoding('binary') // 设置为二进制
req.on('data', (data) => {
console.log(data)
})
req.on('end', () => {
res.end("文件上传成功")
})
})
server.listen(8000, () => {
console.log("服务启动在8000端口")
})
终端控制台打印的内容,后半部分内容如下:
首部内容如下,我们可以通过联系在一起看来我们会发现,其中有很多比较杂乱的内容,这些内容是相对于图片本身来讲多余的部分,例如下方的form-data
的内容很明显是HTTP头部的信息内容,所以导致文件打开的原因就是图片文件二机制数据中混入了其他数据。
二.文件上传正确的✅处理方式
🤡通过上述的分析,我们看到了造成错误的原因是混入的多余数据,那么为了能让图片的数据正常,我们首先需要对文本进行处理,我们的处理过程如下:
js
const http = require("http")
const fs = require("fs")
const server = http.createServer((req, res) => {
req.setEncoding('binary') // 设置为二进制
let formdata = '' // 用来存储数据
req.on('data', (data) => {
formdata += data
// 截取从image/jpeg之后的所有字符串
const imgType = 'image/jpeg'
const imgTypePosition = formdata.indexOf(imgType) + imgType.length // 获取对用索引
let imgData = formdata.substring(imgTypePosition) // 从此位置一直截取到最后
// imgData开始位置会有两个空格,使用正则去除空格
imgData = imgData.replace(/^\s\s*/, '')
// 替换最后的boundary
const regex = /--(.+?)--/ // 使用捕获组
const match = imgData.match(regex)
let boundary = match[1]
imgData = imgData.substring(0, imgData.indexOf(`--${boundary}--`))
// 将处理过的数据进行写入
fs.writeFile('./picture.png', imgData, 'binary', () => {
console.log("文件存储成功")
res.end("文件上传成功~")
})
})
req.on('end', () => {
res.end("文件上传成功")
})
})
server.listen(8000, () => {
console.log("服务启动在8000端口")
})
然后我们在客户端进行上传图片,终端显示文件上传成功。
接下来我们打开我们的图片,看是否能够正常展示,说明文件上传处理成功了。
三.总结
🤗我们在上述通过HTTP模块实现了一个简单流程的文件上传,到那时我们发现,直接上传我们并不能查看图片,是因为图片文件写入了很多多余数据,如果想处理一下我们需要进行对数据进行处理,通过正则匹配,来删除多余的部分,当正确获取数据流之后将这些内容写入就能正常得到上传的图片了。