其他虚的不多说 , 能来看这边博客的,说明就是想知道上传下载是怎么实现的 .
先介绍下这篇博客和这个demo项目说明/介绍
项目名: springboot-upload说明: 上传下载,图片pdf等文件的显示,是项目中最常用的开发 , 每次开发项目的时候都需要去找原来的代码 , 或者 Google很费时间 , 所以称这次又开发到这个功能, 干脆整合springboot 抽取成一个独立项目,初学者可以参考 , 可以不连接数据库. 如果需要整合业务可以连接数据库使用全部功能
介绍: 本项目主要采用springboot , 实现文件上传 , 下载 , 图片,pdf等文件的显示等基本功能 .在基本功能的基础上添加mysql保存文件信息 , 结合resourceId保存绑定业务 , 逻辑删除 ,定时删除无效的文件信息 , 查看文件信息列表等功能
项目如何运行
1. 本项目使用了mysql , 需要运行的话,先创建springboot-upload 数据库2. 执行建表语句file_info.sql (具体如何执行,请另外百度)
3. 修改application.xml , application-sit.xml 数据库的配置文件
4. 启动项目
项目测试方法
1. 本项目没有使用页面,需要测试上传功能,请配合Postman上传2. 文件的下载, 展示可以直接在浏览器中输入地址即可
测试实例:
1. 上传功能
使用Postman , 选择POST请求 , 设置响应头form-data ,选择文件,具体看图地址: http://127.0.0.1:8070/api/file/uploadFile
2. 下载功能
直接在浏览器中输入 上传后的文件名(具体文件名在数据库表中找)地址: http://127.0.0.1:8070/api/file/downloadFile/{fileName}
实例: http://127.0.0.1:8070/api/file/downloadFile/FL_gtU8Vm2t1F03lTn6B.jpg
3. 显示图片/pdf
直接输入地址即可地址: http://127.0.0.1:8070/api/file/view?fileName={fileName}
实例: http://127.0.0.1:8070/api/file/view?fileName=FL_b7Tu2sJTkWQ89uGEr.html
贴核心代码(具体实现请查看码云demo)
说明:1. 博客里面值贴 api , service 的代码.具体详情,请在码云下载 .
FileApi 文件:
package com.dreamhai.springbootupload.api;
import com.dreamhai.springbootupload.exception.BusinessException;
import com.dreamhai.springbootupload.po.FileInfo;
import com.dreamhai.springbootupload.service.FileInfoService;
import com.dreamhai.springbootupload.utils.FileUtils;
import com.dreamhai.springbootupload.utils.ResponseInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Paths;
/**
* @author Dreamhai
* @desc
* @date 2018-06-07 18:42
*/
@Slf4j
@RestController
@RequestMapping(value = "api/file")
public class FileApi {
@Autowired
private FileInfoService fileInfoService;
/**
* 文件上传
* 1. 文件上传后的文件名
* 2. 上传后的文件路径 , 当前年月日时 如:2018060801 2018年6月8日 01时
* 3. file po 类需要保存文件信息 ,旧名 ,大小 , 上传时间 , 是否删除 ,
*
* @param file
* @param request
* @return
*/
@PostMapping("uploadFile")
public ResponseInfo<?> uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
// 判断文件是否为空
if (file.isEmpty()) {
return ResponseInfo.error("文件不能为空");
}
try {
return fileInfoService.upload(file);
} catch (BusinessException e) {
return e.getResponse();
}
}
/**
* 文件下载
* @param fileName
* @param res
*/
@RequestMapping(value = "/downloadFile/{fileName}")
public void downloadFile(@PathVariable("fileName") String fileName, HttpServletResponse res) {
try {
fileInfoService.downloadFile(fileName, res);
} catch (BusinessException e) {
e.getResponse();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 文件查看
*/
@RequestMapping(value = "/view", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> view(@RequestParam("fileName") String fileName){
FileInfo fileInfo = null;
try {
fileInfo = fileInfoService.getImage(fileName);
} catch (IOException e) {
e.printStackTrace();
}
if (fileInfo == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
HttpHeaders header = new HttpHeaders();
if (FileUtils.match(fileInfo.getFileName(), "jpg", "png", "gif", "bmp", "tif")) {
header.setContentType(MediaType.IMAGE_JPEG);
} else if (FileUtils.match(fileInfo.getFileName(), "html", "htm")) {
header.setContentType(MediaType.TEXT_HTML);
} else if (FileUtils.match(fileInfo.getFileName(), "pdf")) {
header.setContentType(MediaType.APPLICATION_PDF);
} else {
header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
}
header.add("X-Filename", fileInfo.getFileName());
header.add("X-MD5", fileInfo.getMd5());
return new ResponseEntity<>(new InputStreamResource(fileInfo.getContent()), header, HttpStatus.OK);
}
/**
* 文件列表查询
*/
@RequestMapping(value = "/find")
public ResponseInfo<?> findList(@RequestParam("resourceId") String resourceId) {
try {
return fileInfoService.findFileList(resourceId);
}catch (BusinessException e){
return e.getResponse();
}
}
/**
* 逻辑删除文件
*/
@RequestMapping(value = "/deleteFile")
public ResponseInfo<?> deleteFile(@RequestParam("fileName") String fileName) {
try {
return fileInfoService.deleteFile(fileName);
}catch (BusinessException e){
return e.getResponse();
}
}
}
package com.dreamhai.springbootupload.service;
import com.dreamhai.springbootupload.configure.UploadConfigure;
import com.dreamhai.springbootupload.dao.FileInfoDao;
import com.dreamhai.springbootupload.exception.BusinessException;
import com.dreamhai.springbootupload.po.FileInfo;
import com.dreamhai.springbootupload.utils.DateUtils;
import com.dreamhai.springbootupload.utils.FileUtils;
import com.dreamhai.springbootupload.utils.ResponseInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.apache.commons.codec.binary.Hex;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
/**
* @author Dreamhai
* @desc
* @date 2018-06-08 11:20
*/
@Slf4j
@Service
public class FileInfoService {
@Autowired
private FileInfoDao fileInfoDao;
@Resource
private UploadConfigure uploadConfigure;
/**
* 上传文件
*
* @param file
* @return
* @throws BusinessException
*/
@Transactional
public ResponseInfo<?> upload(MultipartFile file) throws BusinessException {
//基础路径 E:/springboot-upload/image/
String basePath = uploadConfigure.getBasePath();
//获取文件保存路径 \20180608\113339\
String folder = FileUtils.getFolder();
// 获取前缀为"FL_" 长度为20 的文件名 FL_eUljOejPseMeDg86h.png
String fileName = FileUtils.getFileName() + FileUtils.getFileNameSub(file.getOriginalFilename());
try {
// E:\springboot-upload\image\20180608\113339
Path filePath = Files.createDirectories(Paths.get(basePath, folder));
log.info("path01-->{}", filePath);
//写入文件 E:\springboot-upload\image\20180608\113339\FL_eUljOejPseMeDg86h.png
Path fullPath = Paths.get(basePath, folder, fileName);
log.info("fullPath-->{}", fullPath);
// E:\springboot-upload\image\20180608\113339\FL_eUljOejPseMeDg86h.png
Files.write(fullPath, file.getBytes(), StandardOpenOption.CREATE);
//保存文件信息
FileInfo fileInfo = new FileInfo();
fileInfo.setFileOriginName(file.getOriginalFilename());
fileInfo.setFileType(file.getContentType());
fileInfo.setSize(file.getSize());
fileInfo.setMd5(FileUtils.md5File(fullPath.toString()));
fileInfo.setFileName(fileName);
fileInfo.setFilePath(filePath.toString());
fileInfoDao.save(fileInfo);
} catch (Exception e) {
Path path = Paths.get(basePath, folder);
log.error("写入文件异常,删除文件。。。。", e);
try {
Files.deleteIfExists(path);
} catch (IOException e1) {
e1.printStackTrace();
}
return ResponseInfo.error(e.getMessage());
}
return ResponseInfo.success("上传成功");
}
/**
* 文件下载
*
* @param fileName
* @param res
* @throws BusinessException
* @throws UnsupportedEncodingException
*/
public void downloadFile(String fileName, HttpServletResponse res) throws BusinessException, UnsupportedEncodingException {
if (fileName == null) {
throw new BusinessException("1001", "文件名不能为空");
}
// 通过文件名查找文件信息
FileInfo fileInfo = fileInfoDao.findByFileName(fileName);
log.info("fileInfo-->{}", fileInfo);
if (fileInfo == null) {
throw new BusinessException("2001", "文件名不存在");
}
//设置响应头
res.setContentType("application/force-download");// 设置强制下载不打开
res.addHeader("Content-Disposition", "attachment;fileName=" +
new String(fileInfo.getFileOriginName().getBytes("gbk"), "iso8859-1"));// 设置文件名
res.setHeader("Context-Type", "application/xmsdownload");
//判断文件是否存在
File file = new File(Paths.get(fileInfo.getFilePath(), fileName).toString());
if (file.exists()) {
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
OutputStream os = res.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
log.info("下载成功");
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException("9999", e.getMessage());
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 文件查看
*/
public FileInfo getImage(String fileName) throws IOException {
log.info("fileName-->{}", fileName);
FileInfo fileInfo = fileInfoDao.findByFileNameAndValid(fileName, true);
if (fileInfo == null) {
return null;
}
Path path = Paths.get(fileInfo.getFilePath(), fileInfo.getFileName());
log.info("path-->{}", path);
fileInfo.setContent(Files.newInputStream(path));
return fileInfo;
}
/**
* 根据资源id查询文件信息
* @param resourceId
* @return
* @throws BusinessException
*/
public ResponseInfo<?> findFileList(String resourceId) throws BusinessException {
if (resourceId == null){
throw new BusinessException("1001","资源id不能为空");
}
// 根据资源id查询文件信息
return ResponseInfo.success(fileInfoDao.findByResourceId(resourceId));
}
/**
* 逻辑删除文件
* @param fileName
* @return
* @throws BusinessException
*/
public ResponseInfo<?> deleteFile(String fileName) throws BusinessException{
if (fileName == null){
throw new BusinessException("1001","文件名不能为空");
}
FileInfo fileInfo = fileInfoDao.findByFileName(fileName);
if (fileInfo == null){
throw new BusinessException("2001","文件名:"+fileName+" 不存在");
}
// 逻辑删除文件
fileInfo.setIsDelete(true);
fileInfo.setDeleteTime(DateUtils.getDateString(new Date()));
return ResponseInfo.success(fileInfo);
}
/**
* 每天执行一次,清除无效图片
*/
@Scheduled(cron = "0 0 0 1/1 * ? ")
public void deleteValidFalse() {
//定时删除无效图片信息
List<FileInfo> fileInfos = fileInfoDao.findByValid(false);
fileInfoDao.deleteAll(fileInfos);
log.info("本次删除数据:{}",fileInfos);
}
}
其他代码 ,请查看码云项目
扩展功能说明
1. 增加根据业务id 查找文件列表2. 逻辑删除文件信息
3. 配合定时器 , 每天删除无效文件
4. 后期会更加完善功能 , 请关注码云地址 : https://gitee.com/tanqinghai/springboot-upload