最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實現斷點下載服務端與客戶端的示例代碼

 更新時間:2022年08月21日 15:49:26   作者:Stars-one  
這篇文章主要為大家介紹了如何實現服務端(Spring Boot)與客戶端(Android)的斷點下載與下載續(xù)傳功能,文中的示例代碼講解詳細,需要的可以參考一下

最近在研究斷點下載(下載續(xù)傳)的功能,此功能需要服務端和客戶端進行對接編寫,本篇也是記錄一下關于貼上關于實現服務端(Spring Boot)與客戶端(Android)是如何實現下載續(xù)傳功能

斷點下載功能(下載續(xù)傳)解釋:

客戶端由于突然性網絡中斷等原因,導致的下載失敗,這個時候重新下載,可以繼續(xù)從上次的地方進行下載,而不是重新下載

原理

首先,我們先說明了斷點續(xù)傳的功能,實際上的原理比較簡單

客戶端和服務端規(guī)定好一個規(guī)則,客戶端傳遞一個參數,告知服務端需要數據從何處開始傳輸,服務端接收到參數進行處理,之后文件讀寫流從指定位置開始傳輸給客戶端

實際上,上述的參數,在http協(xié)議中已經有規(guī)范,參數名為Range

而對于服務端來說,只要處理好Range請求頭參數,即可實現下載續(xù)傳的功能

我們來看下Range請求頭數據格式如下:

格式如下:

Range:bytes=300-800
//客戶端需要文件300-800字節(jié)范圍的數據(即500B數據)

Range:bytes=300-
//客戶端需要文件300字節(jié)之后的數據

我們根據上面的格式,服務端對Range字段進行處理(String字符串數據處理),在流中返回指定的數據大小即可

那么,如何讓流返回指定的數據大小或從指定位置開始傳輸數據呢?

這里,Java提供了RandomAccessFile類,通過seekTo()方法,可以讓我們將流設置從指定位置開始讀取或寫入數據

這里讀取和寫入數據,我是采用的Java7之后新增的NIO的Channel進行流的寫入(當然,用傳統(tǒng)的文件IO流(BIO)也可以)

這里,我所說的客戶端是指的Android客戶端,由于App開發(fā)也是基于Java,所以也是可以使用RandomAccessFile這個類

對于客戶端來說,有以下邏輯:

先讀取本地已下載文件的大小,然后請求下載數據將文件大小的數據作為請求頭的數值傳到服務端,之后也是利用RandomAccessFile移動到文件的指定位置開始寫入數據即可

擴展-大文件快速下載思路

利用上面的思路,我們還可以可以得到一個大文件快速下載的思路:

如,一份文件,大小為2000B(這個大小可以通過網絡請求,從返回數據的請求頭content-length獲取獲?。?/p>

客戶端拿回到文件的總大小,根據調優(yōu)算法,將平分成合適的N份,通過線程池,來下載這個N個單文件

在下載完畢之后,將N個文件按照順序合并成單個文件即可

代碼

上面說明了具體的思路,那么下面就是貼出服務端和客戶端的代碼示例

服務端

服務端是采用的spring boot進行編寫

/**
 * 斷點下載文件
 *
 * @return
 */
@GetMapping("download")
public void download( HttpServletRequest request, HttpServletResponse response) throws IOException {
    //todo 這里文件按照你的需求調整
    File file = new File("D:\\temp\\測試文件.zip");
    if (!file.exists()) {
        response.setStatus(HttpStatus.NOT_FOUND.value());
        return;
    }
    long fromPos = 0;
    long downloadSize = file.length();

    if (request.getHeader("Range") != null) {
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        String[] ary = request.getHeader("Range").replaceAll("bytes=", "").split("-");
        fromPos = Long.parseLong(ary[0]);
        downloadSize = (ary.length < 2 ? downloadSize : Long.parseLong(ary[1])) - fromPos;
    }
    //注意下面設置的相關請求頭
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    //相當于設置請求頭content-length
    response.setContentLengthLong(downloadSize);

    //使用URLEncoder處理中文名(否則會出現亂碼)
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("Content-Range", String.format("bytes %s-%s/%s", fromPos, (fromPos + downloadSize), downloadSize));

    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
    randomAccessFile.seek(fromPos);

    FileChannel inChannel = randomAccessFile.getChannel();
    WritableByteChannel outChannel = Channels.newChannel(response.getOutputStream());

    try {
        while (downloadSize > 0) {
            long count = inChannel.transferTo(fromPos, downloadSize, outChannel);
            if (count > 0) {
                fromPos += count;
                downloadSize -= count;
            }
        }
        inChannel.close();
        outChannel.close();
        randomAccessFile.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

客戶端

Android客戶端,是基于Okhttp的網絡框架寫的,需要先引用依賴

implementation 'com.squareup.okhttp3:okhttp:3.9.0'

下面給出的是封裝好的方法(含進度,下載失敗和成功回調):

package com.tyky.update.utils;

import com.blankj.utilcode.util.ThreadUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class FileDownloadUtil {

    public static void download(String url, File file, OnDownloadListener listener) {

        //http://10.232.107.44:9060/swan-business/file/download
        // 利用通道完成文件的復制(非直接緩沖區(qū))
        ThreadUtils.getIoPool().submit(new Runnable() {
            @Override
            public void run() {
                try {

                    //續(xù)傳開始的進度
                    long startSize = 0;
                    if (file.exists()) {
                        startSize = file.length();
                    }
                    OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
                    Request request = new Request.Builder().url(url)
                            .addHeader("Range", "bytes=" + startSize)
                            .get().build();
                    Call call = okHttpClient.newCall(request);
                    Response resp = call.execute();

                    double length = Long.parseLong(resp.header("Content-Length")) * 1.0;
                    InputStream fis = resp.body().byteStream();
                    ReadableByteChannel fisChannel = Channels.newChannel(fis);

                    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
                    //從上次未完成的位置開始下載
                    randomAccessFile.seek(startSize);
                    FileChannel foschannel = randomAccessFile.getChannel();

                    // 通道沒有辦法傳輸數據,必須依賴緩沖區(qū)
                    // 分配指定大小的緩沖區(qū)
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                    // 將通道中的數據存入緩沖區(qū)中
                    while (fisChannel.read(byteBuffer) != -1) {  // fisChannel 中的數據讀到 byteBuffer 緩沖區(qū)中
                        byteBuffer.flip();  // 切換成讀數據模式
                        // 將緩沖區(qū)中的數據寫入通道
                        foschannel.write(byteBuffer);

                        final double progress = (foschannel.size() / length);
                        BigDecimal two = new BigDecimal(progress);
                        double result = two.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
                        //計算進度,回調
                        if (listener != null) {
                            listener.onProgress(result);
                        }
                        byteBuffer.clear();  // 清空緩沖區(qū)
                    }
                    foschannel.close();
                    fisChannel.close();
                    randomAccessFile.close();

                    if (listener != null) {
                        listener.onSuccess(file);
                    }
                } catch (IOException e) {
                    if (listener != null) {
                        listener.onError(e);
                    }

                }
            }
        });


    }

    public interface OnDownloadListener {
        void onProgress(double progress);

        void onError(Exception e);

        void onSuccess(File outputFile);
    }
}

使用:

FileDownloadUtil.download(downloadUrl, file, new FileDownloadUtil.OnDownloadListener() {
    @Override
    public void onProgress(double progress) {
        KLog.d("下載進度: " + progress);
    }

    @Override
    public void onError(Exception e) {
        KLog.e("下載錯誤: " + e.getMessage());
    }

    @Override
    public void onSuccess(File outputFile) {
        KLog.d("下載成功");
    }
});

到此這篇關于Java實現斷點下載服務端與客戶端的示例代碼的文章就介紹到這了,更多相關Java斷點下載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java基礎鞏固系列包裝類代碼實例

    Java基礎鞏固系列包裝類代碼實例

    這篇文章主要介紹了Java包裝類,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • SpringBoot整合WebSocket的客戶端和服務端的實現代碼

    SpringBoot整合WebSocket的客戶端和服務端的實現代碼

    這篇文章主要介紹了SpringBoot整合WebSocket的客戶端和服務端的實現,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • MyBatis自動生成Where語句

    MyBatis自動生成Where語句

    這篇文章主要介紹了MyBatis自動生成Where語句的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-08-08
  • SpringBoot中支持Https協(xié)議的實現

    SpringBoot中支持Https協(xié)議的實現

    本文主要介紹了SpringBoot中支持Https協(xié)議的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • Java判斷字符串中是否包含中文方法

    Java判斷字符串中是否包含中文方法

    這篇文章主要介紹了Java判斷字符串中是否包含中文方法,使用Matcher類解決了這個問題,需要的朋友可以參考下
    2014-06-06
  • java程序員自己的圖片轉文字OCR識圖工具分享

    java程序員自己的圖片轉文字OCR識圖工具分享

    這篇文章主要介紹了java程序員自己的圖片轉文字OCR識圖工具,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java理論基礎Stream元素的匹配與查找

    java理論基礎Stream元素的匹配與查找

    這篇文章主要為大家介紹了java理論基礎Stream元素的匹配與查找方法的示例說明解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • SpringBoot整合Apache?Pulsar教程示例

    SpringBoot整合Apache?Pulsar教程示例

    這篇文章主要為大家介紹了SpringBoot整合Apache?Pulsar教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Java中Easypoi實現excel多sheet表導入導出功能

    Java中Easypoi實現excel多sheet表導入導出功能

    這篇文章主要介紹了Java中Easypoi實現excel多sheet表導入導出功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Linux服務器Java進程消失問題解決

    Linux服務器Java進程消失問題解決

    這篇文章主要介紹了Linux服務器Java進程消失問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11

最新評論

锦州市| 韶山市| 莆田市| 濮阳市| 绥德县| 青阳县| 香格里拉县| 巧家县| 广德县| 都兰县| 龙游县| 德阳市| 安塞县| 辉南县| 淮滨县| 喀什市| 德兴市| 靖西县| 丁青县| 德昌县| 千阳县| 商南县| 太仆寺旗| 兰西县| 榆树市| 泗洪县| 洞头县| 阿巴嘎旗| 伊宁市| 定日县| 新巴尔虎右旗| 武冈市| 宣武区| 财经| 丰镇市| 峨眉山市| 邵阳市| 黄陵县| 七台河市| 蒙山县| 舒兰市|