欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于SpringBoot大文件RestTemplate下載解決方案

 更新時(shí)間:2021年10月25日 08:42:58   作者:人無名,則可專心練劍  
這篇文章主要介紹了SpringBoot大文件RestTemplate下載解決方案,最近結(jié)合網(wǎng)上案例及自己總結(jié),寫了一個(gè)分片下載tuling/fileServer項(xiàng)目,需要的朋友可以參考下

近期基于項(xiàng)目上使用到的RestTemplate下載文件流,遇到1G以上的大文件,下載需要3-4分鐘,因?yàn)檎{(diào)用API接口沒有做分片與多線程, 文件流全部采用同步方式加載,性能很慢。最近結(jié)合網(wǎng)上案例及自己總結(jié),寫了一個(gè)分片下載tuling/fileServer項(xiàng)目: 1.包含同步下載文件流在瀏覽器加載輸出相關(guān)代碼; 2.包含分片多線程下載分片文件及合并文件相關(guān)代碼;

另外在DownloadThread項(xiàng)目中使用代碼完成了一個(gè)遠(yuǎn)程RestUrl請(qǐng)求去獲取一個(gè)遠(yuǎn)端資源大文件進(jìn)行多線程分片下載 到本地的一個(gè)案例,可以下載一些諸如.mp4/.avi等視頻類大文件。相關(guān)代碼也一并打包上傳。

同步下載,支持分片下載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() {
        // 探測(cè)下載
        FileInfo fileInfo = download(0, 10, -1, null);
        if (fileInfo != null) {
            long pages =  fileInfo.fSize / PER_PAGE;
            for (long i = 0; i <= pages; i++) {
                Future<FileInfo> future = taskExecutor.submit(new DownloadThread(i * PER_PAGE, (i + 1) * PER_PAGE - 1, i, fileInfo.fName));
                if (!future.isCancelled()) {
                    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.fName = fName;
        }
    }

    /**
     * 根據(jù)開始位置/結(jié)束位置
     * 分片下載文件,臨時(shí)存儲(chǔ)文件分片
     * 文件大小=結(jié)束位置-開始位置
     *
     * @return
     */
    private FileInfo download(long start, long end, long page, String fName) {
        File dir = new File(DOWN_PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        // 斷點(diǎn)下載
        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();
            // 最后一個(gè)分片
            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();
        }
    }

    /**
     * 獲取遠(yuǎn)程文件尺寸
     */
    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服務(wù)器響應(yīng)錯(cuò)誤!");
            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<FileInfo> {
        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);
        }
    }
}

代碼都在本地親測(cè)(已修復(fù)Bug)可用,目前比較欠缺的是沒有實(shí)現(xiàn)在分片下載時(shí)對(duì)應(yīng)瀏覽器進(jìn)行下載展示,需要暫存在本地磁盤目錄。 目前將代碼開源,希望能有更好解決方案的Coder Fork支持!也歡迎Star捧場(chǎng)。

博文參考了圖靈學(xué)院相關(guān)的分片下載案例教程,并修改了部分代碼實(shí)現(xiàn):

WebUploader--基于SpringBoot搭建,Java文件上傳下載高階實(shí)戰(zhàn)

本文代碼已上傳至GitHub:

BurstDownload

到此這篇關(guān)于SpringBoot大文件RestTemplate下載解決方案的文章就介紹到這了,更多相關(guān)SpringBoot RestTemplate下載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談JavaAPI 中 <E> 與 <T> 的含義

    淺談JavaAPI 中 <E> 與 <T> 的含義

    下面小編就為大家?guī)硪黄獪\談JavaAPI 中 <E> 與 <T> 的含義。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • 淺析Java中String與StringBuffer拼接的區(qū)別

    淺析Java中String與StringBuffer拼接的區(qū)別

    String拼接會(huì)創(chuàng)建一個(gè)新的String對(duì)象,存儲(chǔ)拼接后的字符串,StringBuffer拼接是直接在本身拼接,會(huì)即時(shí)刷新。下面通過本文給大家介紹Java中String與StringBuffer拼接的區(qū)別,感興趣的朋友一起看看吧
    2017-06-06
  • 兩天沒解決的問題chatgpt用了5秒搞定隱藏bug

    兩天沒解決的問題chatgpt用了5秒搞定隱藏bug

    這篇文章主要為大家描述了我用了兩天沒解決的問題chatgpt用了5秒搞定的全程介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 解決微服務(wù)中關(guān)于用戶token處理到的坑

    解決微服務(wù)中關(guān)于用戶token處理到的坑

    這篇文章主要介紹了解決微服務(wù)中關(guān)于用戶token處理到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析

    Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析

    目前有個(gè)需求,需要使用不同的數(shù)據(jù)源,例如某業(yè)務(wù)要用A數(shù)據(jù)源,另一個(gè)業(yè)務(wù)要用B數(shù)據(jù)源,所以下面這篇文章主要給大家介紹了關(guān)于Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析,需要的朋友可以參考下
    2021-12-12
  • SpringBoot獲取HttpServletRequest的3種方式總結(jié)

    SpringBoot獲取HttpServletRequest的3種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于SpringBoot獲取HttpServletRequest的3種方式,在Spring boot項(xiàng)目中經(jīng)常要用到Servlet的常用對(duì)象如HttpServletRequest request,HttpServletResponse response,HttpSession session,需要的朋友可以參考下
    2023-08-08
  • 關(guān)于SpringMVC中控制器如何處理文件上傳的問題

    關(guān)于SpringMVC中控制器如何處理文件上傳的問題

    這篇文章主要介紹了關(guān)于SpringMVC中控制器如何處理文件上傳的問題,在 Web 應(yīng)用程序中,文件上傳是一個(gè)常見的需求,例如用戶上傳頭像、上傳文檔等,本文將介紹 Spring MVC 中的控制器如何處理文件上傳,并提供示例代碼,需要的朋友可以參考下
    2023-07-07
  • java并發(fā)編程專題(八)----(JUC)實(shí)例講解CountDownLatch

    java并發(fā)編程專題(八)----(JUC)實(shí)例講解CountDownLatch

    這篇文章主要介紹了java CountDownLatch的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • SpringBoot配置文件中密碼屬性加密的實(shí)現(xiàn)

    SpringBoot配置文件中密碼屬性加密的實(shí)現(xiàn)

    本文主要介紹了SpringBoot配置文件中密碼屬性加密的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • java 三角形類 Triangle的用法詳解

    java 三角形類 Triangle的用法詳解

    這篇文章主要介紹了java 三角形類 Triangle的用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02

最新評(píng)論