说到web服务器,其实都绕不开文件上传,本次我们将集中讲解
input:file和fs模块在egg项目的应用,讨论如何实现文件上传,并举例——像图片文件该如何在服务端进行处理。
开始文章前建议读者学习了解input控件的file类型和node.js中的fs模块。
客户端上传文件
通常我们使用input控件所具备file类型进行文件上传,分为form表单组合使用和input控件单独使用。
input控件
input:file
<label for="">图片上传:<input id="file" type="file" value="选择图片" multiple></label>
<br>
<button onclick="upload()">上传</button>
Ajax请求
这里我一开始是用XHR进行Ajax请求,但是存在请求服务器无法接收到文件的问题,所以转而使用axios。这里直接使用改代码,如果已经掌握axios自行书写代码。
发送文件使用FormData数据类型,同时需要multipart/form-data作为content-type的参数。
let file = document.querySelector('#file').files[0]
let formData = new FormData()
formData.append("uploadFile", file, file.name)
const config = {
headers: {
"Content-Type": "multipart/form-data;boundary=" + new Date().getTime()
}
};
axios
.post("/file", formData, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
到此我们客户端可以发送完整的post请求,并将file文件以formdata的格式发送给服务器。
服务端接收文件
接受文件
无论发送一个文件还是多个文件,服务器接收到的都是一个file数组。
console.log(ctx.request.files[0])//单个文件或则多个文件时的第一个文件。
- ctx.request.files[0]打印内容:
{
field: 'uploadFile',
filename: '双斯卡蒂.jpg',
encoding: '7bit',
mime: 'image/jpeg',
fieldname: 'uploadFile',
transferEncoding: '7bit',
mimeType: 'image/jpeg',
filepath: 'C:\\Users\\李小由\\AppData\\Local\\Temp\\egg-multipart-tmp\\work2\\2021\\09\\23\\21\\e0bbd897-f1b2-4475-b616-ad4dd7f73ebf.jpg'
}
field、filename、mime等属性我们在生成formdata数据类型进行请求发送前我们已经知道了,关键的是filepath属性,正是因为该属性我们在http请求数据传输成功的时候将文件缓存到filepath所指的地址。这意味着我们可以该copy文件到指定的位置,也可以操作该文件(图片转base64等)。
通过图片缓存地址
filepath,我们得以在图片上传后对其进行获取操作。
base编码存储图片
接下来我们都讨论图片文件的处理。关于base64编码,我们需要使用fs模块。
async index() {
//声明地址变量
const { ctx } = this;
const file = ctx.request.files[0];
//图片处理
let imgbase64 = await fs.readFileSync(file.filepath,'base64')
await fs.unlinkSync(file.filepath)
ctx.body = {
msg: 'ok',
img: imgbase64
}
}
这里我们为了获得上传图片的base64编码信息,于是借助了node内置的fs模块中的编码函数进行处理:
-
readFileSync是同步函数,有两个参数(图片缓存路径,编码方式),返回值为编码结果 -
readFile是异步函数,有三个参数(第三个参数是回调函数),注意和readFileSync不仅是执行顺序不同而且参数也不同。回调函数的参数为编码结果。
这里我们不使用readFile,是因为我们需要将编码结果响应给客户端,如果只是是存入数据库,便可以使用异步的readFileSync,从避免阻塞程序(同步阻塞不明显)。
服务端本地存储图片
使用fs模块的copyFileSync方法,我们可以将客户端请求过来的图片文件进行copy,即保存到自定义的位置,作为本地的永久存储。
async index() {
//多个路径变量
const { ctx } = this;
const file = ctx.request.files[0];
const toFileName = '/public/upload/'+Date.now()+file.filename;
const to = path.dirname(__dirname)+toFileName;
//拷贝图片至本地
await fs.copyFileSync(file.filepath,to)
await fs.unlinkSync(file.filepath)
const newUrl = "http://juejinjin.com:80"+toFileName;
console.log(this.ctx.request.files[0])
ctx.body = {
msg: 'ok',
url: newUrl
}
}
通过unlinkSync函数,我们关闭了filepath,即消除图片缓存,因为我们已经拷贝得到了图片的复制文件。再通过路径组合成可以访问public路由地址响应给客户端,客户端通过这个url便可以直接请求得到图片,也就是图片的网络地址。
整篇文章,其实围绕
formdata数据类型和fs模块进行实现,通过这两个知识点我们便可以以不变应万变,解决在客户端和服务端之间的文件传输问题。至于具体怎么处理传输过来的文件则是具体情况具体讨论大致离不开fs模块。
接下来我们egg基础的学习就告一段落了。我会根据自己学习状态考虑是否深入和深入程度的更新。