java Springboot实现文件下载
package com.example.yjznsb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
@Controller
public class downloadController {
static String utf8 = "utf-8";
@RequestMapping("/download")
@ResponseBody
public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 需要下载的文件,没有设置参数,直接写死了
File file = new File("C:\\Users\\MissLiuAndMrWang\\Desktop\\taoci\\1.sql");
response.setCharacterEncoding(utf8);
// 输入输出流
InputStream is = null;
OutputStream os = null;
try {
// 分片下载 http Range bytes=100-1000
long fSize = file.length();
// 设置为下载的报头
response.setContentType("application/x-download");
// 设置编码
String fileName = URLEncoder.encode(file.getName(),utf8);
response.addHeader("Content-Disposition","attachment;filename="+fileName);
response.setHeader("Accept-Range","bytes");
response.setHeader("fSize",String.valueOf(fSize));
response.setHeader("fName",fileName);
// 分片去读取文件
long pos =0,last = fSize-1,sum = 0;
// 判断是否需要分片下载 如果需要则近if
if(null!=request.getHeader("Range")){
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
String numRange = request.getHeader("Range").replaceAll("bytes=","");
String[] strRange = numRange.split("-");
if(strRange.length == 2){
pos = Long.parseLong(strRange[0].trim());
last = Long.parseLong(strRange[1].trim());
if(last > fSize-1){
last = fSize - 1;
}
}else {
pos = Long.parseLong(numRange.replaceAll("-","").trim());
}
}
long rangeLength = last - pos + 1;
String contentRange = new StringBuffer("bytes ").append(pos).append(last).append("/").append(fSize).toString();
response.setHeader("context-Range",contentRange);
response.setHeader("context-Length",String.valueOf(rangeLength));
os = new BufferedOutputStream(response.getOutputStream());
is = new BufferedInputStream(new FileInputStream(file));
is.skip(pos);
byte[] buffer = new byte[1024];
int length = 0;
// 开始写入
while(sum < rangeLength){
// 每次读取0-1023长度的字符
length = is.read(buffer,0, (rangeLength-sum) <= buffer.length? (int) (rangeLength - sum) :buffer.length);
sum = sum + length;
os.write(buffer,0,length);
}
System.out.println("下载完成");
}finally {
if(is!=null){
is.close();
}
if(os!=null){
os.close();
}
}
}
}
上面直接访问的话无法用到分片下载部分,下面代码是客户端下载时,应用分片下载
package com.example.yjznsb.client;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.*;
import java.net.URLDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RestController
public class DownloadClient {
// 定义每一个分片的大小 5MB
private final static long PER_PAGE=1024 * 1024 * 50;
// 分片存储的临时目录
private final static String DOWN_PATH="C:\\Users\\MissLiuAndMrWang\\Desktop\\springUpload\\src\\main\\java\\com\\example\\yjznsb\\client";
//多线程 定义应该线程值
ExecutorService pool = Executors.newFixedThreadPool(10);
// 文件大小 分片大小 决定分片数量 文件名字
// 探测 实验性的下载获取文件信息
// 使用多线程文件下载
// 最后一个分片下载完 开始合并
@RequestMapping("/downloadClient")
public String downloadFile() throws IOException, InterruptedException {
FIleInfo fIleInfo = download(0,10,-1,null);
if (fIleInfo!=null){
long pages = fIleInfo.fSize/PER_PAGE;
for(long i=0; i < pages;i++){
pool.submit(new Download(i*PER_PAGE,(i+1)*PER_PAGE-1,i,fIleInfo.fName));
}
}
return "success";
}
class Download implements Runnable{
long start ;
long end;
long page;
String fName;
public Download(long start, long end, long page, String fName) {
this.start = start;
this.end = end;
this.page = page;
this.fName = fName;
}
@Override
public void run() {
try {
download(start,end,page,fName);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
class FIleInfo{
long fSize;
String fName;
public FIleInfo(long fSize, String fName) {
this.fSize = fSize;
this.fName = fName;
}
}
// 分片下载的开始位置,结束位置
// 结束位置 - 开始位置 = 分片大小
// 临时存储 分片文件
private FIleInfo download(long start, long end,long page, String fName) throws IOException, InterruptedException {
// 断点下载
File file = new File(DOWN_PATH,page+"-"+fName);
if(file.exists() && page != -1 && file.length() == PER_PAGE){
return null;
}
HttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/download");
httpGet.setHeader("Range","bytes="+start+"-"+end);
HttpResponse response = client.execute(httpGet);
String fSize = response.getFirstHeader("fSize").getValue();
fName = URLDecoder.decode(response.getFirstHeader("fName").getValue());
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
// 输出流
FileOutputStream fos = new FileOutputStream(file);
// 缓冲区
byte[] buffer = new byte[1024];
int ch;
while((ch = is.read(buffer))!=-1){
fos.write(buffer,0,ch);
}
is.close();
fos.flush();
fos.close();
// 判断是否是最后一个分片 如果是 则合并
if(end-Long.parseLong(fSize)>0){
mergeFile(fName,page);
}
return new FIleInfo(Long.parseLong(fSize),fName);
}
// 合并文件部分代码
private void mergeFile(String fName, long page) throws IOException, InterruptedException {
File file = new File(DOWN_PATH,fName);
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
for(int i=0; i<=page; i++){
File tempFile = new File(DOWN_PATH,i+"-"+fName);
while(!file.exists() || (i!=page && tempFile.length()<PER_PAGE)){
Thread.sleep(100);
}
byte[] bytes = FileUtils.readFileToByteArray(tempFile);
os.write(bytes);
os.flush();
tempFile.delete();
File file1 = new File(DOWN_PATH,-1+"-null");
file1.delete();
os.flush();
os.close();
}
}
}