SpringBoot+Vue+ElementUI实现头像上传功能

从安装FastDFS开始,断断续续折腾了两天多,终于基本粗糙地实现了这个功能。
遇到了许多bug,记录一下。

后端:
Controller:
(1)这里要加上 @CrossOrigin解决跨域问题
(2)我的参数的注解是@RequestParam,而不是@RequestBody,这会导致接下来前端使用axios进行POST请求时,参数传不到后台的情况。这个问题在前端解决。
(3)我的设想是前端将图片文件转换成base64编码的字符串,再和图片名称一起传给后台
(4)我在本地的/home/kimi目录下新建了image文件夹

 @CrossOrigin
    @RequestMapping(value = "/uploadbase", method = RequestMethod.POST)
    public String uploadBase64(@RequestParam String base64, @RequestParam String filename) throws Exception {
        String filePath = "/home/kimi/image/";
        FileUtils.base64ToFile(filePath,base64);

        MultipartFile multipartFile = FileUtils.fileToMultipart(filePath,filename);
        String url = fastDFSClientWrapper.uploadFile(multipartFile);
        return url;
    }

FileUtils的base64ToFile方法:

public static boolean base64ToFile(String filePath, String base64Data)  throws Exception {
		String dataPrix = "";
        String data = "";
        
        if(base64Data == null || "".equals(base64Data)){
            return false;
        }else{
            String [] d = base64Data.split("base64,");
            if(d != null && d.length == 2){
                dataPrix = d[0];
                data = d[1];
            }else{
                return false;
            }
        }

        // 因为BASE64Decoder的jar问题,此处使用spring框架提供的工具包
        byte[] bs = Base64Utils.decodeFromString(data);
        // 使用apache提供的工具类操作流
        org.apache.commons.io.FileUtils.writeByteArrayToFile(new File(filePath), bs);
        
        return true;
	}

FileUtils的fileToMultipart方法:
(1)一开始的代码是File file = new File(filePath);,调用这个方法时总是给我报“没有这个文件夹或目录”/ “XXX是一个文件夹”这样的错误,然后我改成了File file = new File(filePath,filename); file.createNewFile();,希望可以在filePath下新建名为filename的文件,这2个参数都是从前端传进来的。
(2)MultipartFile的构造代码一开始是MultipartFile multipartFile = new MockMultipartFile(file.getName(), "png", "image/png", inputStream);,第二个参数本应该是original name,这里直接写成了png,contentType也被简单粗暴地写成了image/png,这样导致后来我在调用multipartFile.getOriginalFilename()方法时,得到的永远是png,而在调用FilenameUtils.getExtension(multipartFile.getOriginalFilename())得到的永远是NULL。于是我改成了MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), "image/png", inputStream);。contentType我还没有详细了解其用法,因此没有改动。

public static MultipartFile fileToMultipart(String filePath,String filename) {
		try {
			// File转换成MutipartFile
			File file = new File(filePath,filename);
			file.createNewFile();
//			if(!file.exists()){
//				file.mkdir();
//			}
			FileInputStream inputStream = new FileInputStream(file);
			MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), "image/png", inputStream);
			return multipartFile;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

FastDFSClientWrapper的uploadFile方法:
(1)storePath即是存储在FastDFS服务器中的位置

 public String uploadFile(MultipartFile multipartFile)throws IOException{
        StorePath storePath = fastFileStorageClient.uploadFile((InputStream)multipartFile.getInputStream(),multipartFile.getSize(), FilenameUtils.getExtension(multipartFile.getOriginalFilename()),null);
        return getResAccessUrl(storePath);
    }

前端:
我使用了ElementUI的upload组件,但是我需要返回给后端的是base64字符串,因此我选择使用http-request来自定义上传行为。
(1)在el-upload节点里写上:http-request="httpRequest"
(2)JS代码:
1)传入httpRequest的options里包括头部、action、file等信息,file里有图片的详细信息,包括uid、name、lastModified、lastModifiedDate等信息。使用let file = options.filelet filename = file.name,file即代表图片文件,filename代表图片名字。
2)新建一个FileReader对象,使用它的readAsDataURL方法读取图像文件,使用let base64Str = this.fileReader.result获取读取到的字符串,这个字符串并不是全都是base64字符串,前面还有data:image之类的一些信息,我们可以使用base64Str.split(',')[1],这样得到的便是base64字符串了。
3)针对上面说过的使用axios进行POST请求时,参数传不到后台的问题,我采用了URLSearchParams解决这个问题。

let param = new URLSearchParams()
          param.append('base64',base64Str.split(',')[1])
          param.append('filename',filename)

在接下来的axios的data里写成:data:param,我一开始写成:

data:{
	param,
}

这样子后端也接收不到参数。
在这段代码里使用了很多Promise的知识,我对这个还不熟练,还要多多学习。

完整代码:

import axios from 'axios';
  export default {
    name: "MyInfo",
    data() {
      return {
        imageUrl: '',
       fileReader:''
      };
    },
    methods: {
      httpRequest(options){
        this.fileReader = new FileReader()
        let file = options.file
        let filename = file.name
        console.log(file)
        console.log(filename)
        if(file){
           this.fileReader.readAsDataURL(file)
        }
        this.fileReader.onload =()=> {
           let base64Str = this.fileReader.result
          let param = new URLSearchParams()
          param.append('base64',base64Str.split(',')[1])
          param.append('filename',filename)
          let config = {
            url: 'http://localhost:8080/uploadbase',
            method: 'post',
            data:param,
            timeout:10000,
          }
          axios(config)
              .then(res=>{
                options.onSuccess(res,file)
              })
              .catch(err=>{
                console.log("error")
                options.onError(err)
              })
        }
      },
    }
  }

当不需要传base64编码给后台时,可以把后台的控制器修改如下,直接用MultipartFile做接收参数即可。

@CrossOrigin
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public String upload(@RequestParam("file")MultipartFile multipartFile) throws IOException {
        String fileUrl = fastDFSClientWrapper.uploadFile(multipartFile);
        return fileUrl;
    }

前端也无需写http-request方法,直接使用element-ui文档贴出的方法即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章