Spring Boot 文件上传(upload) , 下载(download) , 图片显示(view) 最完整的项目

其他虚的不多说 , 能来看这边博客的,说明就是想知道上传下载是怎么实现的 .

先介绍下这篇博客和这个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 

猜你喜欢

转载自blog.csdn.net/dreamhai/article/details/80631821