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測試文件下載成功