Java中常用的9種文件下載方法總結
一、前言
下載文件在我們項目很常見,有下載視頻、文件、圖片、附件、導出Excel、導出Zip壓縮文件等等,這里我對常見的下載做個簡單的總結,主要有文件下載、限速下載、多文件打包下載、URL文件打包下載、Excel導出下載、Excel批量導出Zip包下載、多線程加速下載。
二、搭建Spring Boot項目
搭建個SpringBoot Web項目,引用常用依賴,commons-io作常用IO操作,hutool-all、poi-ooxml做導出Excel操作,commons-compress做多線程壓縮。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.21</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
</dependency>三、文件下載
3.1 單文件下載
最簡單的下載就是提供一個文件下載接口,瀏覽器請求接口后預覽或者下載文件,這里以下載一個1.2G的視頻為例,直接看 /download接口
@GetMapping("/download")
public void download(HttpServletResponse response) throws IOException {
File file = new File("/Users/zxk/Movies/1.2G.mp4");
response.setContentType("video/mp4;charset=utf8");
//設置下載文件名
response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
//中文亂碼處理
//response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8") );
//網頁直接播放
//response.setHeader("Content-Disposition", "inline");
//下載進度
response.setContentLengthLong(file.length());
try (InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()
) {
IOUtils.copy(inputStream, outputStream);
}
}這里有以下幾點需要注意:
- 1. response.setContentType設置文件的類型
- 2. Content-Disposition設置文件下載時顯示的文件名,如果有中文亂碼,需要URLEncode,如果希望瀏覽器直接打開可以設置"inline"
- 3. response.setContentLengthLong(file.length()),設置Http body長度可以在下載時顯示進度
- 4. 下載完成需要關閉流,這里使用try-with-resource自動關閉流
3.2限速下載
使用第一種下載速度會非???,可能瞬間就將你的服務器帶寬占滿了,所以就需要限制下載速度。某盤開會員和不開會員下載速度相差非常大,就是針對不用同步給限制了不同的下載速度
@GetMapping("/limitSpeed")
public void limitSpeed(@RequestParam(value = "speed", defaultValue = "1024") int speed, HttpServletResponse response) throws IOException {
File path = new File("/Users/zxk/Movies/1.2G.mp4");
response.setContentType("video/mp4;charset=utf8");
response.setHeader("Content-Disposition", "attachment;filename=" + path.getName());
response.setContentLengthLong(path.length());
try (
InputStream inputStream = new FileInputStream(path);
OutputStream outputStream = response.getOutputStream()
) {
byte[] buffer = new byte[1024];
int length;
SpeedLimiter speedLimiter = new SpeedLimiter(speed);
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
speedLimiter.delayNextBytes(length);
}
}
}
public class SpeedLimiter {
/** 速度上限(KB/s), 0=不限速 */
private int maxRate = 1024;
private long getMaxRateBytes(){
return this.maxRate * 1024L;
}
private long getLessCountBytes() {
long lcb = getMaxRateBytes() / 10;
if (lcb < 10240) lcb = 10240;
return lcb;
}
public SpeedLimiter(int maxRate) {
this.setMaxRate(maxRate);
}
public synchronized void setMaxRate(int maxRate){
this.maxRate = Math.max(maxRate, 0);
}
private long totalBytes = 0;
private long tmpCountBytes = 0;
private final long lastTime = System.currentTimeMillis();
public synchronized void delayNextBytes(int len) {
if (maxRate <= 0) return;
totalBytes += len;
tmpCountBytes += len;
//未達到指定字節(jié)數跳過...
if (tmpCountBytes < getLessCountBytes()) {
return;
}
long nowTime = System.currentTimeMillis();
long sendTime = nowTime - lastTime;
long workTime = (totalBytes * 1000) / getMaxRateBytes();
long delayTime = workTime - sendTime;
if (delayTime > 0) {
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
tmpCountBytes = 0;
}
}
}3.3多文件打成ZIP包下載
有了單文件下載,肯定就用多文件下載,一般瀏覽器下載多個文件是將多個文件打包成一個Zip文件下載。
@GetMapping("/zip")
public void zip(HttpServletResponse response) throws IOException {
File file1 = new File("/Users/zxk/Movies/2.mp4");
File file2 = new File("/Users/zxk/Movies/2.mp4");
List<File> files = Arrays.asList(file2, file1);
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
zipOutputStream.setLevel(0);
files.forEach(f -> {
try (FileInputStream inputStream = new FileInputStream(f)) {
zipOutputStream.putNextEntry(new ZipEntry(f.getName()));
IOUtils.copy(inputStream, zipOutputStream);
zipOutputStream.closeEntry();
} catch (Exception e) {
e.printStackTrace();
}
});
zipOutputStream.flush();
zipOutputStream.finish();
}
}多文件打成Zip包注意事項:
- 1. zipOutputStream.setLevel(0)設置壓縮等級,0為不壓縮,這樣可以提升下載速度
- 2. 多個文件打包下載時并不是所有文件壓縮完成后才開始下載,而是邊壓縮邊下載,這樣用戶點了下載后,下載任務會直接進入瀏覽器下載列表
3.4整個文件夾下載
有時需要遞歸將整個文件夾打包下載下來
@GetMapping("/dir")
public void dir(HttpServletResponse response) throws IOException {
File dir = new File("/Users/zxk/Movies/download");
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
zipOutputStream.setLevel(0);
zip(zipOutputStream, "", dir);
zipOutputStream.flush();
zipOutputStream.finish();
}
}
public void zip(ZipOutputStream zipOutputStream, String parentPath, File file) throws IOException {
if (file.isDirectory()) {
File[] subFiles = file.listFiles();
if (subFiles != null) {
for (File f : subFiles) {
zip(zipOutputStream, parentPath + file.getName() + "/", f);
}
}
} else {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
zipOutputStream.putNextEntry(new ZipEntry(parentPath + file.getName()));
IOUtils.copy(fileInputStream, zipOutputStream);
}
}
}注意事項:
1. 會遞歸整個文件夾,文件夾文件不能太多
3.5通過URL打包下載
有時我們的文件可能是存在云存儲上,數據庫里存的是文件的ULR,下載時我們就需要通過URL將多個文件打包下載
@GetMapping("/urlZip")
public void urlZip(HttpServletResponse response) throws IOException {
List<String> urls = Arrays.asList("https://demo.com/11666832527556.jpeg",
"https://demo.com/11666831385156.jpeg",
"https://demo.com/11666829917700.jpeg",
"https://demo.com/11666762702021.png",
"https://demo.com/11666762702020.webp",
"https://demo.com/11666549651972.jpg",
"https://demo.com/11666524497476.jpeg",
"https://demo.com/11666507113092.jpg");
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
zipOutputStream.setLevel(0);
urls.forEach(f -> {
try {
zipOutputStream.putNextEntry(new ZipEntry(StringUtils.getFilename(f)));
HttpUtil.download(f, zipOutputStream, false);
zipOutputStream.closeEntry();
} catch (Exception e) {
e.printStackTrace();
}
});
zipOutputStream.flush();
zipOutputStream.finish();
}
}3.6導出Excel下載
有些下載的文件并不存在,而是先從數據庫中查出數據,再動態(tài)生成文件,再提供給用戶下載,這里我們以導出單個Excel文件為例:
@GetMapping("/excel")
public void excel(HttpServletResponse response) throws IOException {
List<List<Integer>> rows = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
rows.add(IntStream.range(i, i + 100).boxed().collect(Collectors.toList()));
}
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=test.xls");
try (OutputStream out = response.getOutputStream();
ExcelWriter writer = ExcelUtil.getWriter()) {
writer.write(rows);
writer.flush(out, true);
}
}3.7批量導出Excel打包下載
很多業(yè)務需要一次性導出多個Excel,這里我們可以將多個Excel壓縮成一個Zip文件下載下來,這里以動態(tài)生成10個Excel主例:
@GetMapping("/excelZip")
public void excelZip(HttpServletResponse response) throws IOException {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
zipOutputStream.setLevel(0);
for (int i = 0; i < 10; i++) {
zipOutputStream.putNextEntry(new ZipEntry(String.format("%s.xls", i)));
try (ExcelWriter writer = ExcelUtil.getWriter()) {
writer.write(generateData());
writer.flush(zipOutputStream);
}
}
zipOutputStream.flush();
zipOutputStream.finish();
}
}
private List<List<Integer>> generateData() {
List<List<Integer>> rows = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
rows.add(IntStream.range(i, i + 100).boxed().collect(Collectors.toList()));
}
return rows;
}3.8多線程加速下載
有時下載數據量比較多,單線程打包會比較慢,這里我們就需要使用多線程并發(fā)打包提高打包速度,這里我以多線程下載多個URL文件為例使用commons-compress的ParallelScatterZipCreator多線程并發(fā)打包
public static final ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();
public static final ExecutorService executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory);
@GetMapping("/parallelZip")
public void excelZipThread(HttpServletResponse response) throws IOException {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
List<String> urls = Arrays.asList("https://demo.com/11671291835144.png",
"https://demo.com/11671291834824.png",
"https://demo.com/11671291833928.png",
"https://demo.com/11671291833800.png",
"https://demo.com/11671291833480.png",
"https://demo.com/11671291828232.png",
"https://demo.com/11671291827528.png",
"https://demo.com/11671291825737.png",
"https://demo.com/11671291825736.png");
ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executor);
try (ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(response.getOutputStream())) {
zipArchiveOutputStream.setLevel(0);
urls.forEach(x -> {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(StringUtils.getFilename(x));
zipArchiveEntry.setMethod(ZipArchiveEntry.STORED);
InputStreamSupplier inputStreamSupplier = () -> URLUtil.getStream(URLUtil.url(x));
parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
});
parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
}3.9多線程批量導Excel打包下載
這種是比較復雜的,動態(tài)生成多個Excel文件后,使用多線程打成ZIP包下載
@GetMapping("/parallelexcelZip")
public void parallelexcelZip(HttpServletResponse response) throws IOException {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executor);
try (ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(response.getOutputStream())) {
zipArchiveOutputStream.setLevel(0);
IntStream.range(1,10).forEach(x -> {
InputStreamSupplier inputStreamSupplier = () ->{
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
try(ExcelWriter writer=ExcelUtil.getWriter()) {
writer.write(generateData());
writer.flush(outputStream);
}
return new ByteArrayInputStream(outputStream.toByteArray());
};
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(String.format("%s.xls",x));
zipArchiveEntry.setMethod(ZipArchiveEntry.STORED);
parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
});
parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
}四、總結
本文主要總結了常用9種常見的文件下載操作,并提供對應的演示代碼,當然還有一些沒有總結到的,如分片下載、斷點結續(xù)下、分布式下載限速等,這些高級下載在普通項目中用到的不太多所以沒有總結。
到此這篇關于Java中常用的9種文件下載方法總結的文章就介紹到這了,更多相關Java文件下載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解Java字節(jié)碼編程之非常好用的javassist
這篇文章主要介紹了詳解Java字節(jié)碼編程之非常好用的javassist,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
解決ResourceBundle.getBundle文件路徑問題
這篇文章主要介紹了解決ResourceBundle.getBundle文件路徑問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
springcloud?gateway實現簡易版灰度路由步驟詳解
這篇文章主要為大家介紹了springcloud?gateway實現簡易版灰度路由步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
Spring Aop之AspectJ注解配置實現日志管理的方法
下面小編就為大家分享一篇Spring Aop之AspectJ注解配置實現日志管理的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01

