国产操作系统生态圈推动信息安全与技术自主发展的新机遇
1197
2022-11-29
关于SpringBoot大文件RestTemplate-解决方案
近期基于项目上使用到的RestTemplate-文件流,遇到1G以上的大文件,-需要3-4分钟,因为调用API接口没有做分片与多线程, 文件流全部采用同步方式加载,性能很慢。最近结合网上案例及自己总结,写了一个分片-tuling/fileServer项目: 1.包含同步-文件流在浏览器加载输出相关代码; 2.包含分片多线程-分片文件及合并文件相关代码;
另外在DownloadThread项目中使用代码完成了一个远程RestUrl请求去获取一个远端资源大文件进行多线程分片- 到本地的一个案例,可以-一些诸如.mp4/.avi等视频类大文件。相关代码也一并打包上传。
同步-,支持分片-Range主要代码:
@Controller
public class DownLoadController {
private static final String UTF8 = "UTF-8";
@RequestMapping("/download")
public void downLoadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
File file = new File("D:\\DevTools\\ideaIU-2021.1.3.exe");
response.setCharacterEncoding(UTF8);
InputStream is = null;
OutputStream os = null;
try {
// 分片- Range表示方式 bytes=100-1000 100-
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 (null != request.getHeader("Range")) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
String numberRange = request.getHeader("Range").replaceAll("bytes=", "");
String[] strRange = numberRange.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(numberRange.replaceAll("-", "").trim());
}
}
long rangeLength = last - pos + 1;
String contentRange = new StringBuffer("bytes").append(pos).append("-").append(last).append("/").append(fSize).toString();
response.setHeader("Content-Range", contentRange);
response.setHeader("Content-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) {
int readLength = (int) (rangeLength - sum);
length = is.read(buffer, 0, (rangeLength - sum) <= buffer.length ? readLength : buffer.length);
sum += length;
os.write(buffer,0, length);
}
System.out.println("-完成");
}finally {
if (is != null){
is.close();
}
if (os != null){
os.close();
}
}
}
}
多线程分片-分片文件,-完成之后合并分片主要代码:
@RestController
public class DownloadClient {
private static final Logger LOGGER = LoggerFactory.getLogger(DownloadClient.class);
private final static long PER_PAGE = 1024L * 1024L * 50L;
private final static String DOWN_PATH = "F:\\fileItem";
ExecutorService taskExecutor = Executors.newFixedThreadPool(10);
@RequestMapping("/downloadFile")
public String downloadFile() {
// 探测-
FileInfo fileInfo = download(0, 10, -1, null);
if (fileInfo != null) {
long pages = fileInfo.fSize / PER_PAGE;
for (long i = 0; i <= pages; i++) {
Future
if (!future.isCancelled()) {
http:// try {
fileInfo = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
return System.getProperty("user.home") + "\\Downloads\\" + fileInfo.fName;
}
return null;
}
class FileInfo {
long fSize;
String fName;
public FileInfo(long fSize, String fName) {
this.fSize = fSize;
this.fNaKmQKjomme = fName;
}
}
/**
* 根据开始位置/结束位置
* 分片-文件,临时存储文件分片
* 文件大小=结束位置-开始位置
*
* @return
*/
private FileInfo download(long start, long end, long page, String fName) {
File dirhttp:// = new File(DOWN_PATH);
if (!dir.exists()) {
dir.mkdirs();
}
// 断点-
File file = new File(DOWN_PATH, page + "-" + fName);
if (file.exists() && page != -1 && file.length() == PER_PAGE) {
return null;
}
try {
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(), "UTF-8");
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);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void mergeFile(String fName, long page) {
File file = new File(DOWN_PATH, fName);
try {
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
for (long i = 0; i <= page; i++) {
File tempFile = new File(DOWN_PATH, i + "-" + fName);
while (!file.exists() || (i != page && tempFile.length() < PER_PAGE)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
byte[] bytes = FileUtils.readFileToByteArray(tempFile);
os.write(bytes);
os.flush();
tempFile.delete();
}
File testFile = new File(DOWN_PATH, -1 + "-null");
testFile.delete();
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取远程文件尺寸
*/
private long getRemoteFileSize(String remoteFileUrl) throws IOException {
long fileSize = 0;
HttpURLConnection httpConnection = (HttpURLConnection) new URL(remoteFileUrl).openConnection();
//使用HEAD方法
httpConnection.setRequestMethod("HEAD");
int responseCode = httpConnection.getResponseCode();
if (responseCode >= 400) {
LOGGER.debug("Web服务器响应错误!");
return 0;
}
String sHeader;
for (int i = 1;; i++) {
sHeader = httpConnection.getHeaderFieldKey(i);
if (sHeader != null && sHeader.equals("Content-Length")) {
LOGGER.debug("文件大小ContentLength:" + httpConnection.getContentLength());
fileSize = Long.parseLong(httpConnection.getHeaderField(sHeader));
break;
}
}
return fileSize;
}
class DownloadThread implements Callable
long start;
long end;
long page;
String fName;
public DownloadThread(long start, long end, long page, String fName) {
this.start = start;
this.end = end;
this.page = page;
this.fName = fName;
}
@Override
public FileInfo call() {
return download(start, end, page, fName);
}
}
}
代码都在本地亲测(已修复Bug)可用,目前比较欠缺的是没有实现在分片-时对应浏览器进行-展示,需要暂存在本地磁盘目录。 目前将代码开源,希望能有更好解决方案的Coder Fork支持!也欢迎Star捧场。
博文参考了图灵学院相关的分片-案例教程,并修改了部分代码实现:
WebUploader--基于SpringBoot搭建,java文件上传-高阶实战
本文代码已上传至github:
BurstDownload
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~