核心控制器:通过实现文件上传下载的控制器FileController,分别包含文件的上传和下载方法
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ethan.exam_ms.common.R;
import com.ethan.exam_ms.entity.Files;
import com.ethan.exam_ms.service.FilesService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* 文件处理接口
*/
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
//注入yaml中的属性
@Value("${exam.path}")
private String basePath;
@Value("${server.ip}")
private String serverIp;
@Autowired
private FilesService filesService;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
public R<Files> upload(@RequestParam("file") MultipartFile file){
//file是一个临时文件,需要转存指定位置,否则本次请求结束会删除临时文件
log.info(file.toString());
//原始文件名
String originalFilename=file.getOriginalFilename();
//获得原始文件名的后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
long size = file.getSize();
//使用UUID重新生成文件名,防止文件名称重复造成文件的覆盖
String fileName= UUID.randomUUID().toString()+suffix;
File uploadFile = new File(basePath + fileName);
//创建一个目录对象
File dir=new File(basePath);
if (!dir.exists()){
dir.mkdirs();
}
String url = null;
String md5=null;
try {
// 获取文件的md5
md5= SecureUtil.md5(file.getInputStream());
// 从数据库查询是否存在相同的记录
Files dbFiles = getFileByMd5(md5);
if (dbFiles != null) {
url = dbFiles.getUrl();
} else {
//将临时文件转存到指定位置
file.transferTo(uploadFile);
url = "http://" + serverIp + ":9090/file/" + fileName;
}
} catch (IOException e) {
e.printStackTrace();
}
// 存储数据库,可省略,只是作为数据库文件管理
Files saveFile = new Files();
saveFile.setName(originalFilename);
saveFile.setType(suffix);
saveFile.setSize(size/1024); // 单位 kb
saveFile.setUrl(url);
saveFile.setMd5(md5);
filesService.save(saveFile);
return R.success(saveFile) ;
}
/**
* 图片上传
* @param file
* @return
*/
@PostMapping("/uploadImg")
public Object uploadImg(@RequestParam("file") MultipartFile file){
//file是一个临时文件,需要转存指定位置,否则本次请求结束会删除临时文件
log.info(file.toString());
//原始文件名
String originalFilename=file.getOriginalFilename();
//获得原始文件名的后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
long size = file.getSize();
//使用UUID重新生成文件名,防止文件名称重复造成文件的覆盖
String fileName= UUID.randomUUID().toString()+suffix;
File uploadFile = new File(basePath + fileName);
//创建一个目录对象
File dir=new File(basePath);
if (!dir.exists()){
dir.mkdirs();
}
String url = null;
String md5 = null;
try {
// 获取文件的md5
md5= SecureUtil.md5(file.getInputStream());
// 从数据库查询是否存在相同的记录
Files dbFiles = getFileByMd5(md5);
if (dbFiles != null) {
url = dbFiles.getUrl();
} else {
//将临时文件转存到指定位置
file.transferTo(uploadFile);
url = "http://" + serverIp + ":9090/file/" + fileName;
}
} catch (IOException e) {
e.printStackTrace();
}
// 存储数据库
Files saveFile = new Files();
saveFile.setName(originalFilename);
saveFile.setType(suffix);
saveFile.setSize(size/1024); // 单位 kb
saveFile.setUrl(url);
saveFile.setMd5(md5);
filesService.save(saveFile);
/* JSONUtil.parseObj("{\"errno\":0,\"data\":[{\"url\":\""+url+"\"}]}")*/
JSONObject jsonObject=new JSONObject();
JSONObject jsonObject1=new JSONObject();
jsonObject1.set("url",url);
jsonObject.set("errno",0);
JSONArray data =new JSONArray();
data.set(jsonObject1);
jsonObject.set("data",data);
return jsonObject;
}
/**
* 文件下载
* @param fileName
* @param response
*/
@GetMapping("/{fileName}")
public void download(@PathVariable String fileName, HttpServletResponse response) throws IOException{
try {
//输入流,通过输入流读取文件内容
FileInputStream fileInputStream=new FileInputStream((new File(basePath+fileName)));
//输出流,通过输出流将文件写回浏览器
ServletOutputStream outputStream=response.getOutputStream();
int len=0;
byte[] bytes=new byte[1024];
while ((len=fileInputStream.read(bytes))!=-1) {
outputStream.write(bytes,0,len);
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 通过文件的md5查询文件
* @param md5
* @return
*/
private Files getFileByMd5(String md5) {
// 查询文件的md5是否存在
QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("md5", md5);
List<Files> filesList = filesService.list(queryWrapper);
return filesList.size() == 0 ? null : filesList.get(0);
}
}
利用这个控制器我们就可以实现文件上传,比如上传头像,前端上传代码:
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
:action="http://localhost:9090/file/upload"
ref="img"
:show-file-list="false"
:on-success="handleImgUploadSuccess"
:before-upload="beforeImgUploadSuccess"
>
<img v-if="userForm.avatar" :src="userForm.avatar" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
当然,你可以对上传的文件进行一系列限制,这里用到的一些属性名可以根据自己表单的信息修改
handleImgUploadSuccess(res) {
this.userForm.avatar = res.data.url
},
beforeImgUploadSuccess(file) {
let typeInfo = ["image/jpeg", "image/png"];
const isLt5M = file.size / 1024 / 1024 < 5;
const isMagic = typeInfo.includes(file.type);
if (!typeInfo.includes(file.type)) {
this.$message.error('上传头像图片只能是 JPG 或 PNG格式!');
}
if (!isLt5M) {
this.$message.error('上传头像图片大小不能超过 5MB!');
}
return isMagic && isLt5M;
},
也可以实现文件下载,这里的url会以路径参数的形式调用FileController的download方法
window.open(url)