前言
在做网页的时候需要用到图片上传,之前做图片上传时使用的时vant的组件,比较简单。直接传到后端一个base64的码,在后端解码然后返回给前端就好了,但是这次做图片上传不能用组件,要使用vue3自带的input来上传图片。然后,就开始了长达数小时的折磨,一个图片上传搞得我头都大了,幸而最后解决了问题,那么话不多说,开始吧。
前端vue3
<template>
<input
type="file"
class="upload"
@change="addImg"
ref="inputer"
accept="image/png,image/jpeg,image/gif,image/jpg"
/>
</template>
<script lang="ts">
import axios from "axios";
export default {
setup() {
function addImg(e) {
const file = e.target.files[0]; //获得input上传的文件
uploadImg(file); //将文件上传到后端的方法
}
//上传图片
function uploadImg(img) {
const formData = new FormData(); //使用formData上传文件
formData.append("file", img);
axios({
method: "POST",
url: "/api/user/info",
headers: { "Content-Type": "multipart/form-data" },
data: formData,
}).then((res) => {
console.log(res.data.md5);
});
}
return {
addImg,
uploadImg,
};
},
};
</script>
前端的代码较为简单,主要注意的点就是fromData在传送时不要用花括号包裹 。如:
{data:fromData}
其次是注意传送时的headers,我们传送的是表单数据,如果在后端想以表单数据被接受,headers就要写成代码中的样子。
headers: { "Content-Type": "multipart/form-data" }
在写这段代码时去网页上查看请求的负载,你可能会看到一个非常简洁的页面:
我一开始看到的时候认为是数据没有传送,但事实上是已经传送了的,至于为什么会这么“简洁”,暂时还未弄明白,而且关系不是很大。(强烈建议用火狐,火狐就没有这种情况)
前端就是这么简洁了,在我所写的代码中,后端在拿到前端送来的图片后,会下载图片然后把图片的名字(以MD5加密后的字符串)返还给前端,前端将此图片名与图片访问路径拼接后得到完整的图片路径(在这里我并没有这么做,但我会给出后端应如何加密及返回md5字符串)。
golang后端
后端要做的事情就有点多了,首先是提取前端的图片数据,然后是对数据进行md5加密,得到图片名,其后是创建文件并将文件名返回给前端。
import (
"bufio"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type Message struct {
Content []Content `json:"content"`
Err string `json:"err"`
Sign int `json:"sign"`
MD5 string `json:"md5"`
}
type Content struct {
Cid string `json:"cid"`
Title string `json:"title"`
About string `json:"about"`
Content string `json:"content""`
}
func MD5(v interface{}) string {//获取md5字符串
a := v.(string)
d := []byte(a)
m := md5.New()
m.Write(d)
return hex.EncodeToString(m.Sum(nil))
}
func Picture(w http.ResponseWriter, r *http.Request) {
var a Message
if err := r.ParseMultipartForm(r.ContentLength); err != nil {
return
}
image := r.MultipartForm.File["file"][0] //获得文件数据,file为前端传送数据时自定义的键
file, err := image.Open()
if err == nil {
data, err := ioutil.ReadAll(file) // 读取二进制文件字节流
if err == nil {
k := MD5(string(data)) //对图片进行md5加密(得到图片的名称)
fmt.Println(k)
name := fmt.Sprintf("%s%s", k, ".jpg") //生成图片名
fmt.Println(name)
dec := string(data) //将第i位置的字符到最后一个字符进行bas64解码,得到图片
f, err := os.Create("./uploaded/a/" + name) //生成图片,记得创建文件夹
if err != nil {
log.Println(err)
}
defer f.Close()
//写入文件时,使用带缓存的 *Writer
write := bufio.NewWriter(f)
write.WriteString(dec)
//Flush将缓存的文件真正写入到文件中
write.Flush()
a.MD5 = name
resp(w, &a) //发送图片名称给前端
}
}
}
func resp(w http.ResponseWriter, msg *Message) {
if w == nil || msg == nil {
fmt.Println("call respErr with invalid w/msg")
return
}
//将msg结构体转化为json字符串
buf, err := json.Marshal(&msg)
if err != nil {
w.Write([]byte(fmt.Sprintf(`{"code":-300,"msg":"%s"}`, err.Error())))
fmt.Println(err.Error())
return
}
//fmt.Println(buf)
w.Write(buf)
}
golang的后端中提供了对fromData进行解析的方法,你可以使用
if err := r.ParseMultipartForm(r.ContentLength); err != nil {
return
}
image := r.MultipartForm.File["file"][0] //获得文件数据
file, err := image.Open()
data, err := ioutil.ReadAll(file) // 读取二进制文件字节流
来获取文件的二进制数据,也可以使用
if err := r.ParseMultipartForm(r.ContentLength); err != nil {
return
}
file, header, err := r.FormFile("file")
fmt.Println("FormFile(file): ", file, header, err)
来获取文件,其效果是:
一样可以获得二进制字节,但是需要做一下字符匹配(大概?)
总结
图片上传暂时就到这吧,后面可能会优化一下上传及下载。