从安装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.file
和let 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文档贴出的方法即可。