提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
JAVA文件上传与下载
前言
在实际项目开发中,涉及到图片、视频、文档等上传与下载的情况十分普遍,本文就简单介绍文件上传下载的相关内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、文件上传
文件上传接口接收了前端传过来文件,需要把文件存储在本地,另一方面存储在数据库文件表中,最后将文件表中的文件ID返回给前端,前端就可以拿着文件ID走业务的新增、修改接口,将文件数据和业务数据进行绑定。
文件表建表语句(mysql):
DROP TABLE IF EXISTS T_FILE;
CREATE TABLE T_FILE(
ID VARCHAR(32) NOT NULL COMMENT '主键' ,
FILE_NAME VARCHAR(32) COMMENT '文件名' ,
CONTENT VARCHAR(32) COMMENT '远程文件id' ,
ENTITY_ID VARCHAR(32) COMMENT '对应id' ,
ENTITY_TYPE INT COMMENT '文件类型' ,
TYPE VARCHAR(32) COMMENT '文件后缀' ,
SHOW_INDEX INT COMMENT '显示顺序' ,
CREATED_BY VARCHAR(32) COMMENT '创建人' ,
CREATED_TIME DATETIME COMMENT '创建时间' ,
UPDATED_BY VARCHAR(32) COMMENT '更新人' ,
UPDATED_TIME DATETIME COMMENT '更新时间' ,
PRIMARY KEY (ID)
) COMMENT = '文件表';
代码如下:
控制层代码:
@PostMapping("/upload")
@ApiOperation(value = "文件上传"")
@ResponseBody
public BaseResponse uploadFile(@RequestPart("file") MultipartFile multipartFile) {
try {
return uploadFileService.upload(multipartFile);
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException(e.getMessage());
}
}
ServiceImpl代码
/**
* 上传文件
*
* @param multipartFile
* @return
* @throws Exception
*/
@Override
public BaseResponse upload(MultipartFile multipartFile) throws Exception {
if (multipartFile == null) {
log.error("Please choose an image to upload..");
return BaseResponse.getFail("Please choose an image to upload..");
}
String originalFilename = multipartFile.getOriginalFilename();
FileResponse fileResponse = new FileResponse();
String fileUUID = saveFileToLocal(multipartFile);
fileResponse.setId(fileUUID);
fileResponse.setFileName(originalFilename);
return BaseResponse.getSuccess(fileResponse);
}
/**
* 上传到本地文件
*
* @param multipartFile
* @return
*/
private String saveFileToLocal(MultipartFile multipartFile) {
File databaseDir = new File(databasePath);
if (!databaseDir.exists()) {
databaseDir.mkdir();
}
String fileUUID = UUID.randomUUID().toString().toLowerCase().replace("-", "");
String fileType = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1);
try (FileOutputStream fileOutputStream = new FileOutputStream(databasePath + "/" + fileUUID + "." + fileType)) {
org.apache.commons.io.IOUtils.copy(multipartFile.getInputStream(), fileOutputStream);
FileEntity fileEntity = new FileEntity();
fileEntity.setId(fileUUID);
fileEntity.setFileName(multipartFile.getOriginalFilename());
fileEntity.setContent(fileUUID);
fileEntity.setType(fileType);
fileEntity.setCreatedTime(DateUtils.getCurrentTime());
fileEntity.setCreatedBy(jwtUtils.getUserId());
save(fileEntity);
return fileUUID;
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
二、文件下载
将文件上传返回的文件ID传入,将文件数据以流的方式进行返回
控制层代码
@GettMapping("/download")
@ApiOperation( "文件下载")
@ResponseBody
public void downloadFile(String fileUUID, HttpServletResponse response) {
try {
uploadFileService.downloadFile(fileUUID, response);
} catch (Exception e) {
throw new BusinessException(e.getMessage());
}
}
serviceImpl代码
/**
* 下载文件
*
* @param fileUUID
* @param response
* @throws Exception
*/
@Override
public void downloadFile(String fileUUID, HttpServletResponse response) throws Exception {
InputStream inputStream = null;
try {
FileEntity fileEntity = getById(fileUUID);
if (fileEntity == null) {
throw new BusinessException("Can't find file info by fileUUID:" + fileUUID);
}
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileEntity.getFileName());
OutputStream outputStream = response.getOutputStream();
inputStream = downloadFileByLocalUUID(fileEntity);
IOUtils.copy(inputStream, outputStream);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
/**
* 从本地下载文件
*
* @param fileEntity
* @return
*/
private InputStream downloadFileByLocalUUID(FileEntity fileEntity) {
try {
String filePath = databasePath + "/" + fileEntity.getContent() + "." + fileEntity.getType();
File file = new File(filePath);
if (file.exists()) {
return new FileInputStream(file);
} else {
throw new BusinessException("Can't fild the file: " + file.getAbsolutePath());
}
} catch (Exception e) {
throw new BusinessException("Download file error:" + e.toString());
}
}
拓展
当数据库存储与文件相关内容时,有两种做法,一则是通过上述方式通过业务ID和业务类型将业务和文件查询出来,二则是直接业务表中直接存储url,在文件表中维护具体的信息,这两种方式各有优劣。
直接存储URL:
优点:简单易用;减少查询次数;易于维护(存储位置不变)
缺点:安全性较低;URL变更困难(全路径);冗余问题
业务ID和业务类型:
优点:灵活性高;安全性高;便于扩展(额外的标签信息)
缺点:增加复杂性(查询次数);性能开销
结论:如果项目较小,对安全性和灵活性要求不高,直接存储URL更为合适;对于大型项目,对安全性、灵活性要求比较高,可以用存储ID的方式