springboot文件上傳、下載

1、定義文件上傳接口的返回值

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.UUID;

/**
 * UploadResult
 *
 * @desc: TODO 類的設計目的、功能及注意事項
 * @version:
 * @createTime: 2020/4/30 9:54
 * @author:
 */
@Getter
@AllArgsConstructor(access= AccessLevel.PRIVATE)
public class UploadResult {

    private final static String UTF_8 = "UTF-8";

    /**
     * 文件下載的接口地址,與後面要寫的文件下載接口相呼應
     */
    private final static String DOWNLOAD_API = "/api/file/download";

    /**
     * filePath 與後面要寫的文件下載接口相呼應
     */
    private final static String FILE_PATH_KEY = "filePath";

    /**
     * rename 與後面要寫的文件下載接口相呼應
     */
    private final static String RENAME_KEY = "rename";



    /**
     * 文件名(原文件名稱,文件存儲到服務器時將對他重命名)
     */
    private final String fileName;

    /**
     * 文件後綴
     */
    private final String fileSuffix;

    /**
     * 文件大小
     */
    private final long fileSize;

    /**
     * 文件路徑(相對於文件上傳配置的存儲目錄)
     */
    private final String filePath;

    /**
     * 文件下載路徑(附帶rename參數,使下載的文件仍使用原文件名)
     */
    private final String downloadPath;

    /**
     * newInstance
     * @desc: TODO 描述這個方法的功能、適用條件及注意事項
     * @author:
     * @createTime: 2020/4/30 15:02
     * @param multipartFile
     * @param folder
     * @return: UploadResult
     */
    public static UploadResult newInstance(MultipartFile multipartFile, String folder) {
        String fileName = multipartFile.getOriginalFilename();
        String fileSuffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        long fileSize = multipartFile.getSize();
        String filePath = (StringUtils.isEmpty(folder) ? "" : (folder +  File.separator)) + UUID.randomUUID().toString().replaceAll("-","") + fileSuffix;
        try {
            String downloadPath = MessageFormat.format("{0}?{1}={2}&{3}={4}", DOWNLOAD_API, FILE_PATH_KEY, URLEncoder.encode(filePath, UTF_8), RENAME_KEY, URLEncoder.encode(fileName, UTF_8));
            return new UploadResult(fileName, fileSuffix, fileSize, filePath, downloadPath);
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException("URLEncoder error", e);
        }
    }


}

2、創建文件下載的幫助類DownloadHelp

import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.MessageFormat;

/**
 * DownloadHelp
 * @desc: TODO 類的設計目的、功能及注意事項
 * @version:
 * @createTime: 2020/4/30 14:11
 * @author: 
 */
@Slf4j
public class DownloadHelp {

    private File file;

    private long startByte;

    private long endByte;

    private int httpServletResponseStatus;

    private long contentLength;

    private DownloadHelp(File file, long startByte, long endByte, int httpServletResponseStatus){
        this.file = file;
        this.startByte = startByte;
        this.endByte = endByte;
        this.httpServletResponseStatus = httpServletResponseStatus;
        this.contentLength = endByte - startByte + 1;
    }

    private void setHeaders(HttpServletResponse response, String rename) {
        if(httpServletResponseStatus>0){
            response.setStatus(httpServletResponseStatus);
        }
        String contentType = "application/octet-stream";
        response.setContentType(contentType);
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Type", contentType);
        response.setHeader("Content-Length", String.valueOf(contentLength));
        response.setHeader("Content-Range", MessageFormat.format("bytes {0}-{1}/{2}", startByte, endByte, file.length()));
        try {
            response.setHeader("Content-Disposition", MessageFormat.format("attachment;filename={0}", URLEncoder.encode(StringUtils.isEmpty(rename) ? file.getName() : rename, "UTF-8")));
        } catch (UnsupportedEncodingException e) {
            log.warn("set Content-Disposition header error", e);
        }
    }

    private void download(HttpServletResponse response) {
        BufferedOutputStream outputStream = null;
        RandomAccessFile randomAccessFile = null;
        long transmitted = 0;
        try {
            randomAccessFile = new RandomAccessFile(file, "r");
            outputStream = new BufferedOutputStream(response.getOutputStream());
            byte[] buff = new byte[4096];
            int len = 0;
            randomAccessFile.seek(startByte);
            while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) {
                outputStream.write(buff, 0, len);
                transmitted += len;
            }
            if (transmitted < contentLength) {
                len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted));
                outputStream.write(buff, 0, len);
                transmitted += len;
            }
            outputStream.flush();
            log.info(MessageFormat.format("下載完畢:{0}-{1}:{2}", startByte, endByte, transmitted));
        } catch (ClientAbortException e) {
            log.error(MessageFormat.format("用戶停止下載:{0}-{1}:{2}", startByte, endByte, transmitted));
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
            if (randomAccessFile != null) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
    }

    public static DownloadHelp newInstance(String filePath, String range){
        File file = new File(filePath);
        long startByte = 0;
        long endByte = file.length() - 1;
        int httpServletResponseStatus = 0;
        if (range != null && range.contains("bytes=") && range.contains("-")) {
            range = range.substring(range.lastIndexOf("=") + 1).trim();
            String[] ranges = range.split("-");
            if (range.startsWith("-")) {
                endByte = Long.parseLong(ranges[1]);
            }else if (range.endsWith("-")) {
                startByte = Long.parseLong(ranges[0]);
            }else {
                startByte = Long.parseLong(ranges[0]);
                endByte = Long.parseLong(ranges[1]);
            }
            httpServletResponseStatus = HttpServletResponse.SC_PARTIAL_CONTENT;
        }
        return new DownloadHelp(file, startByte, endByte, httpServletResponseStatus);
    }

    public void download(HttpServletResponse response, String rename) {
        this.setHeaders(response, rename);
        this.download(response);
    }

}

3、定義文件服務接口IFileService

import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;

/**
 * IFileService
 *
 * @desc: TODO 類的設計目的、功能及注意事項
 * @version:
 * @createTime: 2020/4/30 9:52
 * @author: 
 */
public interface IFileService {

    UploadResult upload(MultipartFile multipartFile, String folder);

    default UploadResult upload(MultipartFile multipartFile){
        return upload(multipartFile, "");
    }

    void download(String filePath, String rename, String range, HttpServletResponse response);
}

4、創建文件服務接口實現類FileServiceImpl,並在配置文件application.yml中配置文件存儲路徑

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

/**
 * FileServiceImpl
 *
 * @desc: TODO 類的設計目的、功能及注意事項
 * @version:
 * @createTime: 2020/4/30 9:58
 * @author: 
 */
@Slf4j
@Service
public class FileServiceImpl implements IFileService {

    @Value("${upload.path}")
    private String uploadPath;

    private static void mkdirsIfFolderNotExists(File folder){
        if(!folder.exists()){
            folder.mkdirs();
        }
    }

    @Override
    public UploadResult upload(MultipartFile multipartFile, String folder) {
        Assert.notNull(multipartFile, "未上傳任何文件");
        UploadResult uploadResult = UploadResult.newInstance(multipartFile, folder);
        File file = new File(uploadPath, uploadResult.getFilePath());
        mkdirsIfFolderNotExists(file.getParentFile());
        try {
            multipartFile.transferTo(file);
        } catch (IOException e) {
            throw new BusinessException("文件上傳失敗", e);
        }
        return uploadResult;
    }

    @Override
    public void download(String filePath, String rename, String range, HttpServletResponse response) {
        Assert.hasLength(filePath, "文件路徑不能爲空");
        DownloadHelp.newInstance(uploadPath + File.separator + filePath, range).download(response, rename);
    }

}
upload:
	path: E:\server-store-file

5、創建文件上傳下載的接口類FileApi

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

/**
 * FileApi
 *
 * @desc: TODO 類的設計目的、功能及注意事項
 * @version:
 * @createTime: 2020/4/30 10:16
 * @author: 
 */
@RestController
@RequestMapping("/api/file")
public class FileApi {

    @Autowired
    private IFileService fileService;

    @PostMapping( value = "/upload")
    @ResponseBody
    public AjaxResult<UploadResult> upload(
            MultipartFile file,
            String folder
    ) {
        return AjaxResult.success(fileService.upload(file, folder));
    }

    @GetMapping("/download")
    public void download(
            @RequestParam("filePath") String filePath,
            @RequestParam(value = "rename", required = false) String rename,
            @RequestHeader(value = "Range", required = false) String range,
            HttpServletResponse response
    ){
        fileService.download(filePath, rename, range, response);
    }

}

6、使用之前配置的swagger-ui測試接口
瀏覽器輸入http://127.0.0.1:8080/swagger-ui.html找到我們上傳接口,測試結果如下:
在這裏插入圖片描述
再複製downloadPath到http://localhost:8080後面並在瀏覽器上打開即http://localhost:8080/api/file/download?filePath=2020%2F05%2F01%5C5dee902da3144010bd41d0fbbbb76943.png&rename=xixian.png測試文件下載成功

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章