一.前端上传图片的方式
- 表单提交方式
- HTML5方式(base64、blob)
表单提交方式
<!DOCTYPE html>
<html>
<body>
<h2>HTML Forms</h2>
<form action="/file" method="POST" enctype="multipart/form-data">
<label for="fname">First name:</label><br />
<input type="text" id="fname" name="fname" value="张" /><br />
<label for="lname">Last name:</label><br />
<input type="text" id="lname" name="lname" value="三" /><br /><br />
<label for="file">file:</label><br />
<input type="file" id="file" name="file" /><br /><br />
<input type="submit" value="Submit" />
</form>
</body>
</html>
这里简单写了一个表单提交的html代码,这里需要注意的是,如果不指定enctype=”multipart/form-data”,他默认会以 application/x-www-form-urlencoded这种编码方式进行网络请求,form-data格式请求会用一个boundary分隔符区分。在表单传输中如果不指定form-data格式会无法传输文件。这个也比较好理解,因为如果用fname=xxx&file=xxx这种格式传输会有几个问题,1.文件名等信息需要单独和服务端定一套新的上传规范。2.文件传输很大,拼在file后面有很大不确定性。
表单提交对应的服务端程序(express示例)
const fileUpload = require('express-fileupload')
app.post(
"/file",
fileUpload(),
(req, res) => {
console.log(req.files, req.body)
req.files.file.mv(path.resolve(__dirname, 'upload/a.jpg'))
res.send("ok")
}
)
HTML5方式
1.base64
<!DOCTYPE html>
<html>
<body>
<h2>HTML Forms</h2>
<div>
<label for="file">file:</label><br>
<input type="file" id="file" name="file"><br><br>
<input onClick='submit()' type="submit" value="Submit">
</div>
<script>
let upload = {}
function submit(){
console.log('here', upload)
fetch('/submitb64', {
method : "POST",
//body : `name=${upload.name}&data=${upload.data}`,
body : JSON.stringify(upload),
headers : {
//'Content-Type' : 'application/x-www-form-urlencoded'
'Content-Type' : 'application/json'
}
})
}
document.getElementById("file").addEventListener('change', e=>{
const files = e.target.files
for(let file of files) {
const fr = new FileReader()
fr.readAsDataURL(file)
fr.onload = () => {
console.log(fr.result)
upload.data = fr.result.substr(22)
upload.name = file.name
}
}
})
</script>
</body>
</html>
如果使用base64的方式来传输文件,需要利用FileReader来对file类型进行转换。读取base64后前端做了一个substr字符串删除,来删除前缀的base64描述,也就是data:image/png;base64, 这个部分。之后就是真正的base64编码了。转换之后传输我们需要选择一个合适的传输方式,在上述代码中有注释一个片段,是用x-www-form-urlencoded方式传输,这是很多开发者可能会下意识想到的方法,至于为什么不能用这种方式上面已经讲过。通常来说我们会选择json的方式来进行传输base64文件。
html5base64对应的服务端程序(express示例)
const bodyParser = require('body-parser')
app.post(
"/submitb64",
bodyParser.json(),
(req, res) => {
const buffer = new Buffer(req.body.data, 'base64')
console.log(req.body.data.length)
fs.writeFileSync(
path.resolve(__dirname, "upload/x.jpg"),
buffer)
res.send("ok")
}
)
2.blob
众所周知,base64编码会将文件体积变大,因此在传输一些较大的文件时不建议使用这种方式来传输。因此html5中还可以利用blob的方法来传输。
<!DOCTYPE html>
<html>
<body>
<h2>HTML Forms</h2>
<div>
<label for="file">file:</label><br>
<input type="file" id="file" name="file"><br><br>
<input onClick='submit()' type="submit" value="Submit">
</div>
<script>
let upload = {}
function submit(){
console.log('here', upload)
const formData = new FormData()
formData.append('name', upload.name)
formData.append('file', upload.data)
fetch('/submitbinary', {
method : "POST",
body : formData,
headers : {
// 这里如果设置了Content-Type会有Bug
// 'Content-Type' : 'multipart/form-data'
}
})
}
document.getElementById("file").addEventListener('change', e=>{
const files = e.target.files
for(let file of files) {
upload.data = file
upload.name = file.name
}
})
</script>
</body>
</html>
这里注意有个大坑,fetch请求中不要加’Content-Type’ : ‘multipart/form-data’这个header,如果加了这个header会导致请求中content-type中后面没有boundary,导致服务端无法解析。fetch在发送请求中如果发现body中是form-data的时候,会自己加上这个编码格式的header并且自动加上boundary。
html5 blob对应的服务端程序(express示例)
app.post(
"/submitbinary",
fileUpload(),
(req, res) => {
console.log(req.files, req.body)
req.files.file.mv(path.resolve(__dirname, 'upload/b.jpg'))
res.send("ok")
}
)
该方法的优点:1.传输比base64更快。2.对cpu压力更小。 因此没有特殊原因的情况下通常用blob上传。