SpringBoot項目的多文件兼多線程上傳下載
前言
我們的項目目前需要在一個相冊中,上傳多個的圖片,因此,在一次的用戶提交過程中,會有多張的圖片需要被處理,那么此時,就需要有一個方法,來處理這多張文件。
很容易可以想到MultipartFile,我們在使用POST請求的時候就知道文件的單張上傳都是POST請求加上一個@RequestParam的MultipartFile類型的文件。
如下


但是上面只能實現(xiàn)單張文件的上傳,因此為了確保效率以及以及提交就能完成多文件的上傳,需要把代碼修改為如下狀態(tài),也就是請求參數(shù)為一個數(shù)組,這樣子就能接受多文件的請求了

文件上傳到本地代碼編寫
先最簡單的介紹一下把文件保存到本地的代碼編寫
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{ //判斷文件名長度是否過長
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
//判斷文件的擴展類型是否合理
assertAllowed(file, allowedExtension);
//對文件名進行編碼
String fileName = extractFilename(file);
//獲取文件在本機的絕對地址
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
//將文件放到本機絕對地址
file.transferTo(Paths.get(absPath));
//返回文件名
return getPathFileName(fileName);
}
//比較重要的就是這個 他將會創(chuàng)建多級目錄 并且把文件保存到對應(yīng)的位置
private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
{
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists())
{
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
}
return desc.isAbsolute() ? desc : desc.getAbsoluteFile();
}
文件上傳之后,目錄結(jié)構(gòu)如下

其中文件目錄結(jié)構(gòu)指定位置為

全局線程池配置
我使用的SpringBoot版本為2.7.7,并且這是一個SpringCloud項目,因此,我對各種不同場景下的線程池進行了不同的配置,并且在需要使用到線程池的模塊中直接映入這個線程池配置模塊即可,代碼如下,注意,這個代碼是我自己編寫的,很多類都是Java中沒有的,你們按照自己的方法編寫線程池配置即可
@AutoConfiguration
public class DynamicThreadPool {
/**
* 初始化線程池
*
* @return
*/
@Bean("fileThreadPool")
private static ThreadPoolExecutor buildThreadPoolExecutor() {
return new ThreadPoolExecutor(3,
3,
60,
TimeUnit.SECONDS,
new ResizableCapacityLinkedBlockIngQueue<Runnable>(10),
new NamedThreadFactory("file-thread-"),
new ThreadPoolExecutor.DiscardPolicy());
}
}
然后將這個類自動加載

實現(xiàn)多線程上傳
其實實現(xiàn)多線程上傳比較簡單,很容易的就可以想到Thread類,ThreadPoolExecutor,Semaphore,CountdownLaunch,CompletableFuture,CyclicBarrier等各種解決方法。
這里先簡單的列出CountDownLaunch配合ThreadPoolExecutor來實現(xiàn)多線程文件上傳的方法
CountDownLaunch
CountDownLatch 有什么用?
CountDownLatch 允許 count 個線程阻塞在一個地方,直至所有線程的任務(wù)都執(zhí)行完畢。CountDownLatch 是一次性的,計數(shù)器的值只能在構(gòu)造方法中初始化一次,之后沒有任何機制再次對其設(shè)置值,當 CountDownLatch 使用完畢后,它不能再次被使用。
在這個項目中,我們要讀取處理 3個文件,這 3個任務(wù)都是沒有執(zhí)行順序依賴的任務(wù),但是我們需要返回給用戶的時候?qū)⑦@幾個文件的處理的結(jié)果進行統(tǒng)計整理。為此我們定義了一個線程池和 count 為3的CountDownLatch對象 。使用線程池處理讀取任務(wù),每一個線程處理完之后就將 count-1,調(diào)用CountDownLatch對象的 await()方法,直到所有文件讀取完之后,才會接著執(zhí)行后面的邏輯。
代碼比較簡單,也很好理解,也就是每一個文件拷貝完畢之后,調(diào)用countDownLaunch的countDown方法將計數(shù)器-1即可。
同時,由于是多線程拷貝,并且我需要保存每一次拷貝的返回結(jié)果,因此,就使用到了CopyOnWriteArrayList來保證并發(fā)情況下的數(shù)據(jù)集合不被丟失修改。
@Autowired
@Qualifier("fileThreadPool")
private ThreadPoolExecutor fileThreadPool;
/**
* 實現(xiàn)多文件多線程上傳
*
* @param files 要上傳的文件
* @return 返回戀愛日志信息
*/
@ApiOperation(value = "多附件上傳-純附件上傳", notes = "多附件上傳")
@ResponseBody
@PostMapping("/uploadFiles")
public R<LoveLogs> handleFileUpload(@RequestParam("files") MultipartFile[] files) {
LoveLogs loveLogs = new LoveLogs();
String[] urls = new String[files.length];
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
CountDownLatch countDownLatch = new CountDownLatch(files.length);
for (int i = 0; i < files.length; i++) {
try {
獲取文件名
//String fileName = file.getOriginalFilename();
拼接文件保存路徑
//String filePath = "D:/uploads/" + fileName;
保存文件到本地
//file.transferTo(new File(filePath));
MultipartFile file = files[i];
//String url = sysFileService.uploadFile(file);
//urls[i] = url;
//TODO 使用CountDownLaunch或者ComplatableFuture或者Semaphore
//來完成多線程的文件上傳
fileThreadPool.submit(() -> {
try {
String s = sysFileService.uploadFile(file);
list.add(s);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//表示一個文件已經(jīng)被完成
countDownLatch.countDown();
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
try {
//阻塞直到所有的文件完成復(fù)制
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//統(tǒng)計每個文件的url
String photoUrls = String.join(",", list);
loveLogs.setUrls(photoUrls);
//返回結(jié)果
return R.ok(loveLogs);
}
實測
然后就是上傳多個文件,并且發(fā)送請求了,下面是一個簡單的請求模板,可以發(fā)現(xiàn)我上傳完畢文件之后,他返回給我了本地的這個文件的位置,當然,這個文件的url地址啥的你們自己與前端對接完畢即可,我這里是多個url直接封裝在一起并且使用英文逗號作為分隔符,具體情況自定義即可。

文件回顯
文件的下載回顯也比較簡單,只要給出文件對應(yīng)的位置,然后直接去本地加載即可。
這里文件回顯暫時不做多線程優(yōu)化,等后期項目需要了在做
/**
* 文件下載
*
* @param name 文件名稱
* @param response 響應(yīng)流
*/
@GetMapping("/download")
public void download(@RequestParam String name, HttpServletResponse response) {
//FileInputStream fis = null;
//ServletOutputStream os = null;
try (FileInputStream fis = new FileInputStream(new File(name));
ServletOutputStream os = response.getOutputStream()) {
//輸入流,通過輸入流讀取文件內(nèi)容
//fis = new FileInputStream(new File( name));
//輸出流,通過輸出流將文件寫回瀏覽器,在瀏覽器展示圖片
//os = response.getOutputStream();
//設(shè)置響應(yīng)的數(shù)據(jù)的格式
response.setContentType("image/jpeg");
int len = 0;
byte[] buffre = new byte[1024 * 10];
while ((len = fis.read(buffre)) != -1) {
os.write(buffre, 0, len);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}

到此這篇關(guān)于SpringBoot項目的多文件兼多線程上傳下載的文章就介紹到這了,更多相關(guān)SpringBoot 多文件上傳下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+Email發(fā)送郵件的實現(xiàn)示例
Spring?Boot提供了簡單而強大的郵件發(fā)送功能,本文主要介紹了SpringBoot+Email發(fā)送郵件的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下2024-03-03
javafx tableview鼠標觸發(fā)更新屬性詳解
這篇文章主要為大家詳細介紹了javafx tableview鼠標觸發(fā)更新屬性的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
java多線程中的volatile和synchronized用法分析
這篇文章主要介紹了java多線程中的volatile和synchronized用法分析,以實例的形式分析了在多線程中volatile和synchronized的用法區(qū)別與使用原理,具有一定的參考借鑒價值,需要的朋友可以參考下2014-12-12
SpringMVC 方法四種類型返回值總結(jié)(你用過幾種)
這篇文章主要介紹了SpringMVC 方法四種類型返回值總結(jié)(你用過幾種),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05

