国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring-Boot實(shí)現(xiàn)HTTP大文件斷點(diǎn)續(xù)傳分片下載-大視頻分段漸進(jìn)式播放

這篇具有很好參考價(jià)值的文章主要介紹了Spring-Boot實(shí)現(xiàn)HTTP大文件斷點(diǎn)續(xù)傳分片下載-大視頻分段漸進(jìn)式播放。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

服務(wù)端如何將一個(gè)大視頻文件做切分,分段響應(yīng)給客戶端,讓瀏覽器可以漸進(jìn)式地播放。

Spring Boot實(shí)現(xiàn)HTTP分片下載斷點(diǎn)續(xù)傳,從而實(shí)現(xiàn)H5頁面的大視頻播放問題,實(shí)現(xiàn)漸進(jìn)式播放,每次只播放需要播放的內(nèi)容就可以了,不需要加載整個(gè)文件到內(nèi)存中。

文件的斷點(diǎn)續(xù)傳、文件多線程并發(fā)下載(迅雷就是這么玩的)等。文章來源地址http://www.zghlxwxcb.cn/news/detail-511593.html

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-bom</artifactId>
        <version>5.8.18</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-core</artifactId>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>

代碼實(shí)現(xiàn)

ResourceController

package com.example.insurance.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import com.example.insurance.common.ContentRange;
import com.example.insurance.common.MediaContentUtil;
import com.example.insurance.common.NioUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 內(nèi)容資源控制器
 */
@SuppressWarnings("unused")
@Slf4j
@RestController("resourceController")
@RequestMapping(path = "/resource")
public class ResourceController {

    /**
     * 獲取文件內(nèi)容
     *
     * @param fileName 內(nèi)容文件名稱
     * @param response 響應(yīng)對(duì)象
     */
    @GetMapping("/media/{fileName}")
    public void getMedia(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response,
                         @RequestHeader HttpHeaders headers) {
//        printRequestInfo(fileName, request, headers);

        String filePath = MediaContentUtil.filePath();
        try {
            this.download(fileName, filePath, request, response, headers);
        } catch (Exception e) {
            log.error("getMedia error, fileName={}", fileName, e);
        }
    }

    /**
     * 獲取封面內(nèi)容
     *
     * @param fileName 內(nèi)容封面名稱
     * @param response 響應(yīng)對(duì)象
     */
    @GetMapping("/cover/{fileName}")
    public void getCover(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response,
                         @RequestHeader HttpHeaders headers) {
//        printRequestInfo(fileName, request, headers);

        String filePath = MediaContentUtil.filePath();
        try {
            this.download(fileName, filePath, request, response, headers);
        } catch (Exception e) {
            log.error("getCover error, fileName={}", fileName, e);
        }
    }


    // ======= internal =======

    private static void printRequestInfo(String fileName, HttpServletRequest request, HttpHeaders headers) {
        String requestUri = request.getRequestURI();
        String queryString = request.getQueryString();
        log.debug("file={}, url={}?{}", fileName, requestUri, queryString);
        log.info("headers={}", headers);
    }

    /**
     * 設(shè)置請(qǐng)求響應(yīng)狀態(tài)、頭信息、內(nèi)容類型與長(zhǎng)度 等。
     * <pre>
     * <a >
     *     HTTP/1.1 Range Requests</a>
     * 2. Range Units
     * 4. Responses to a Range Request
     *
     * <a >
     *     HTTP/1.1</a>
     * 10.2.7 206 Partial Content
     * 14.5 Accept-Ranges
     * 14.13 Content-Length
     * 14.16 Content-Range
     * 14.17 Content-Type
     * 19.5.1 Content-Disposition
     * 15.5 Content-Disposition Issues
     *
     * <a >
     *     Content-Disposition</a>
     * 2. The Content-Disposition Header Field
     * 2.1 The Inline Disposition Type
     * 2.3 The Filename Parameter
     * </pre>
     *
     * @param response     請(qǐng)求響應(yīng)對(duì)象
     * @param fileName     請(qǐng)求的文件名稱
     * @param contentType  內(nèi)容類型
     * @param contentRange 內(nèi)容范圍對(duì)象
     */
    private static void setResponse(
            HttpServletResponse response, String fileName, String contentType,
            ContentRange contentRange) {
        // http狀態(tài)碼要為206:表示獲取部分內(nèi)容
        response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
        // 支持?jǐn)帱c(diǎn)續(xù)傳,獲取部分字節(jié)內(nèi)容
        // Accept-Ranges:bytes,表示支持Range請(qǐng)求
        response.setHeader(HttpHeaders.ACCEPT_RANGES, ContentRange.BYTES_STRING);
        // inline表示瀏覽器直接使用,attachment表示下載,fileName表示下載的文件名
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "inline;filename=" + MediaContentUtil.encode(fileName));
        // Content-Range,格式為:[要下載的開始位置]-[結(jié)束位置]/[文件總大小]
        // Content-Range: bytes 0-10/3103,格式為bytes 開始-結(jié)束/全部
        response.setHeader(HttpHeaders.CONTENT_RANGE, contentRange.toContentRange());

        response.setContentType(contentType);
        // Content-Length: 11,本次內(nèi)容的大小
        response.setContentLengthLong(contentRange.applyAsContentLength());
    }

    /**
     * <a >
     *     Spring Boot 處理 HTTP Headers</a>
     */
    private void download(
            String fileName, String path, HttpServletRequest request, HttpServletResponse response,
            HttpHeaders headers)
            throws IOException {
        Path filePath = Paths.get(path + fileName);
        if (!Files.exists(filePath)) {
            log.warn("file not exist, filePath={}", filePath);
            return;
        }
        long fileLength = Files.size(filePath);
//        long fileLength2 = filePath.toFile().length() - 1;
//        // fileLength=1184856, fileLength2=1184855
//        log.info("fileLength={}, fileLength2={}", fileLength, fileLength2);

        // 內(nèi)容范圍
        ContentRange contentRange = applyAsContentRange(headers, fileLength, request);

        // 要下載的長(zhǎng)度
        long contentLength = contentRange.applyAsContentLength();
        log.debug("contentRange={}, contentLength={}", contentRange, contentLength);

        // 文件類型
        String contentType = request.getServletContext().getMimeType(fileName);
        // mimeType=video/mp4, CONTENT_TYPE=null
        log.debug("mimeType={}, CONTENT_TYPE={}", contentType, request.getContentType());

        setResponse(response, fileName, contentType, contentRange);

        // 耗時(shí)指標(biāo)統(tǒng)計(jì)
        StopWatch stopWatch = new StopWatch("downloadFile");
        stopWatch.start(fileName);
        try {
            // case-1.參考網(wǎng)上他人的實(shí)現(xiàn)
//            if (fileLength >= Integer.MAX_VALUE) {
//                NioUtils.copy(filePath, response, contentRange);
//            } else {
//                NioUtils.copyByChannelAndBuffer(filePath, response, contentRange);
//            }

            // case-2.使用現(xiàn)成API
            NioUtils.copyByBio(filePath, response, contentRange);
//            NioUtils.copyByNio(filePath, response, contentRange);

            // case-3.視頻分段漸進(jìn)式播放
//            if (contentType.startsWith("video")) {
//                NioUtils.copyForBufferSize(filePath, response, contentRange);
//            } else {
//                // 圖片、PDF等文件
//                NioUtils.copyByBio(filePath, response, contentRange);
//            }
        } finally {
            stopWatch.stop();
            log.info("download file, fileName={}, time={} ms", fileName, stopWatch.getTotalTimeMillis());
        }
    }

    private static ContentRange applyAsContentRange(
            HttpHeaders headers, long fileLength, HttpServletRequest request) {
        /*
         * 3.1. Range - HTTP/1.1 Range Requests
         * https://www.rfc-editor.org/rfc/rfc7233#section-3.1
         * Range: "bytes" "=" first-byte-pos "-" [ last-byte-pos ]
         *
         * For example:
         * bytes=0-
         * bytes=0-499
         */
        // Range:告知服務(wù)端,客戶端下載該文件想要從指定的位置開始下載
        List<HttpRange> httpRanges = headers.getRange();

        String range = request.getHeader(HttpHeaders.RANGE);
        // httpRanges=[], range=null
        // httpRanges=[448135688-], range=bytes=448135688-
        log.debug("httpRanges={}, range={}", httpRanges, range);

        // 開始下載位置
        long firstBytePos;
        // 結(jié)束下載位置
        long lastBytePos;
        if (CollectionUtils.isEmpty(httpRanges)) {
            firstBytePos = 0;
            lastBytePos = fileLength - 1;
        } else {
            HttpRange httpRange = httpRanges.get(0);
            firstBytePos = httpRange.getRangeStart(fileLength);
            lastBytePos = httpRange.getRangeEnd(fileLength);
        }
        return new ContentRange(firstBytePos, lastBytePos, fileLength);
    }
}

NioUtils

package com.example.insurance.common;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.NioUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.io.unit.DataSize;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;

/**
 * NIO相關(guān)工具封裝,主要針對(duì)Channel讀寫、拷貝等封裝
 */
@Slf4j
public final class NioUtils {

    /**
     * 緩沖區(qū)大小 16KB
     *
     * @see NioUtil#DEFAULT_BUFFER_SIZE
     * @see NioUtil#DEFAULT_LARGE_BUFFER_SIZE
     */
//    private static final int BUFFER_SIZE = NioUtil.DEFAULT_MIDDLE_BUFFER_SIZE;
    private static final int BUFFER_SIZE = (int) DataSize.ofKilobytes(16L).toBytes();

    /**
     * <pre>
     * <a >
     *     Java后端實(shí)現(xiàn)視頻分段漸進(jìn)式播放</a>
     * 服務(wù)端如何將一個(gè)大的視頻文件做切分,分段響應(yīng)給客戶端,讓瀏覽器可以漸進(jìn)式地播放。
     * 文件的斷點(diǎn)續(xù)傳、文件多線程并發(fā)下載(迅雷就是這么玩的)等。
     *
     * <a >
     *     大文件分片上傳前后端實(shí)現(xiàn)</a>
     * </pre>
     */
    public static void copyForBufferSize(
            Path filePath, HttpServletResponse response, ContentRange contentRange) {
        String fileName = filePath.getFileName().toString();

        RandomAccessFile randomAccessFile = null;
        OutputStream outputStream = null;
        try {
            // 隨機(jī)讀文件
            randomAccessFile = new RandomAccessFile(filePath.toFile(), "r");
            // 移動(dòng)訪問指針到指定位置
            randomAccessFile.seek(contentRange.getStart());

            // 注意:緩沖區(qū)大小 2MB,視頻加載正常;1MB時(shí)有部分視頻加載失敗
            int bufferSize = BUFFER_SIZE;

            //獲取響應(yīng)的輸出流
            outputStream = new BufferedOutputStream(response.getOutputStream(), bufferSize);

            // 每次請(qǐng)求只返回1MB的視頻流
            byte[] buffer = new byte[bufferSize];
            int len = randomAccessFile.read(buffer);
            //設(shè)置此次相應(yīng)返回的數(shù)據(jù)長(zhǎng)度
            response.setContentLength(len);
            // 將這1MB的視頻流響應(yīng)給客戶端
            outputStream.write(buffer, 0, len);

            log.info("file download complete, fileName={}, contentRange={}",
                    fileName, contentRange.toContentRange());
        } catch (ClientAbortException | IORuntimeException e) {
            // 捕獲此異常表示用戶停止下載
            log.warn("client stop file download, fileName={}", fileName);
        } catch (Exception e) {
            log.error("file download error, fileName={}", fileName, e);
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(randomAccessFile);
        }
    }

    /**
     * 拷貝流,拷貝后關(guān)閉流。
     *
     * @param filePath     源文件路徑
     * @param response     請(qǐng)求響應(yīng)
     * @param contentRange 內(nèi)容范圍
     */
    public static void copyByBio(
            Path filePath, HttpServletResponse response, ContentRange contentRange) {
        String fileName = filePath.getFileName().toString();

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(filePath.toFile(), "r");
            randomAccessFile.seek(contentRange.getStart());

            inputStream = Channels.newInputStream(randomAccessFile.getChannel());
            outputStream = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);

            StreamProgress streamProgress = new StreamProgressImpl(fileName);

            long transmitted = IoUtil.copy(inputStream, outputStream, BUFFER_SIZE, streamProgress);
            log.info("file download complete, fileName={}, transmitted={}", fileName, transmitted);
        } catch (ClientAbortException | IORuntimeException e) {
            // 捕獲此異常表示用戶停止下載
            log.warn("client stop file download, fileName={}", fileName);
        } catch (Exception e) {
            log.error("file download error, fileName={}", fileName, e);
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(inputStream);
        }
    }

    /**
     * 拷貝流,拷貝后關(guān)閉流。
     * <pre>
     * <a >
     *     Java NIO 學(xué)習(xí)筆記(一)----概述,Channel/Buffer</a>
     * </pre>
     *
     * @param filePath     源文件路徑
     * @param response     請(qǐng)求響應(yīng)
     * @param contentRange 內(nèi)容范圍
     */
    public static void copyByNio(
            Path filePath, HttpServletResponse response, ContentRange contentRange) {
        String fileName = filePath.getFileName().toString();

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(filePath.toFile(), "r");
            randomAccessFile.seek(contentRange.getStart());

            inputStream = Channels.newInputStream(randomAccessFile.getChannel());
            outputStream = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);

            StreamProgress streamProgress = new StreamProgressImpl(fileName);

            long transmitted = NioUtil.copyByNIO(inputStream, outputStream,
                    BUFFER_SIZE, streamProgress);
            log.info("file download complete, fileName={}, transmitted={}", fileName, transmitted);
        } catch (ClientAbortException | IORuntimeException e) {
            // 捕獲此異常表示用戶停止下載
            log.warn("client stop file download, fileName={}", fileName);
        } catch (Exception e) {
            log.error("file download error, fileName={}", fileName, e);
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(inputStream);
        }
    }

    /**
     * <pre>
     * <a >
     *     SpringBoot Java實(shí)現(xiàn)Http方式分片下載斷點(diǎn)續(xù)傳+實(shí)現(xiàn)H5大視頻漸進(jìn)式播放</a>
     * SpringBoot 實(shí)現(xiàn)Http分片下載斷點(diǎn)續(xù)傳,從而實(shí)現(xiàn)H5頁面的大視頻播放問題,實(shí)現(xiàn)漸進(jìn)式播放,每次只播放需要播放的內(nèi)容就可以了,不需要加載整個(gè)文件到內(nèi)存中。
     * 二、Http分片下載斷點(diǎn)續(xù)傳實(shí)現(xiàn)
     * 四、緩存文件定時(shí)刪除任務(wù)
     * </pre>
     */
    public static void copy(Path filePath, HttpServletResponse response, ContentRange contentRange) {
        String fileName = filePath.getFileName().toString();
        // 要下載的長(zhǎng)度
        long contentLength = contentRange.applyAsContentLength();

        BufferedOutputStream outputStream = null;
        RandomAccessFile randomAccessFile = null;
        // 已傳送數(shù)據(jù)大小
        long transmitted = 0;
        try {
            randomAccessFile = new RandomAccessFile(filePath.toFile(), "r");
            randomAccessFile.seek(contentRange.getStart());
            outputStream = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
            // 把數(shù)據(jù)讀取到緩沖區(qū)中
            byte[] buffer = new byte[BUFFER_SIZE];

            int len = BUFFER_SIZE;
            //warning:判斷是否到了最后不足4096(buffer的length)個(gè)byte這個(gè)邏輯((transmitted + len) <= contentLength)要放前面
            //不然會(huì)會(huì)先讀取randomAccessFile,造成后面讀取位置出錯(cuò);
            while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
                transmitted += len;

                log.info("fileName={}, transmitted={}", fileName, transmitted);
            }
            //處理不足buffer.length部分
            if (transmitted < contentLength) {
                len = randomAccessFile.read(buffer, 0, (int) (contentLength - transmitted));
                outputStream.write(buffer, 0, len);
                transmitted += len;

                log.info("fileName={}, transmitted={}", fileName, transmitted);
            }

            log.info("file download complete, fileName={}, transmitted={}", fileName, transmitted);
        } catch (ClientAbortException e) {
            // 捕獲此異常表示用戶停止下載
            log.warn("client stop file download, fileName={}, transmitted={}", fileName, transmitted);
        } catch (Exception e) {
            log.error("file download error, fileName={}, transmitted={}", fileName, transmitted, e);
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(randomAccessFile);
        }
    }

    /**
     * 通過數(shù)據(jù)傳輸通道和緩沖區(qū)讀取文件數(shù)據(jù)。
     * <pre>
     * 當(dāng)文件長(zhǎng)度超過{@link Integer#MAX_VALUE}時(shí),
     * 使用{@link FileChannel#map(FileChannel.MapMode, long, long)}報(bào)如下異常。
     * java.lang.IllegalArgumentException: Size exceeds Integer.MAX_VALUE
     *   at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:863)
     *   at com.example.insurance.controller.ResourceController.download(ResourceController.java:200)
     * </pre>
     *
     * @param filePath     源文件路徑
     * @param response     請(qǐng)求響應(yīng)
     * @param contentRange 內(nèi)容范圍
     */
    public static void copyByChannelAndBuffer(
            Path filePath, HttpServletResponse response, ContentRange contentRange) {
        String fileName = filePath.getFileName().toString();
        // 要下載的長(zhǎng)度
        long contentLength = contentRange.applyAsContentLength();

        BufferedOutputStream outputStream = null;
        FileChannel inChannel = null;
        // 已傳送數(shù)據(jù)大小
        long transmitted = 0;
        long firstBytePos = contentRange.getStart();
        long fileLength = contentRange.getLength();
        try {
            inChannel = FileChannel.open(filePath, StandardOpenOption.READ, StandardOpenOption.WRITE);
            // 建立直接緩沖區(qū)
            MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, firstBytePos, fileLength);
            outputStream = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
            // 把數(shù)據(jù)讀取到緩沖區(qū)中
            byte[] buffer = new byte[BUFFER_SIZE];

            int len = BUFFER_SIZE;
            // warning:判斷是否到了最后不足4096(buffer的length)個(gè)byte這個(gè)邏輯((transmitted + len) <= contentLength)要放前面
            // 不然會(huì)會(huì)先讀取file,造成后面讀取位置出錯(cuò)
            while ((transmitted + len) <= contentLength) {
                inMap.get(buffer);
                outputStream.write(buffer, 0, len);
                transmitted += len;

                log.info("fileName={}, transmitted={}", fileName, transmitted);
            }
            // 處理不足buffer.length部分
            if (transmitted < contentLength) {
                len = (int) (contentLength - transmitted);
                buffer = new byte[len];
                inMap.get(buffer);
                outputStream.write(buffer, 0, len);
                transmitted += len;

                log.info("fileName={}, transmitted={}", fileName, transmitted);
            }

            log.info("file download complete, fileName={}, transmitted={}", fileName, transmitted);
        } catch (ClientAbortException e) {
            // 捕獲此異常表示用戶停止下載
            log.warn("client stop file download, fileName={}, transmitted={}", fileName, transmitted);
        } catch (Exception e) {
            log.error("file download error, fileName={}, transmitted={}", fileName, transmitted, e);
        } finally {
            IoUtil.close(outputStream);
            IoUtil.close(inChannel);
        }
    }

}

ContentRange

package com.example.insurance.common;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 內(nèi)容范圍對(duì)象
 * <pre>
 * <a >
 *     4.2. Content-Range - HTTP/1.1 Range Requests</a>
 * Content-Range: "bytes" first-byte-pos "-" last-byte-pos  "/" complete-length
 *
 * For example:
 * Content-Range: bytes 0-499/1234
 * </pre>
 *
 * @see org.apache.catalina.servlets.DefaultServlet.Range
 */
@Getter
@AllArgsConstructor
public class ContentRange {

    /**
     * 第一個(gè)字節(jié)的位置
     */
    private final long start;
    /**
     * 最后一個(gè)字節(jié)的位置
     */
    private long end;
    /**
     * 內(nèi)容完整的長(zhǎng)度/總長(zhǎng)度
     */
    private final long length;

    public static final String BYTES_STRING = "bytes";

    /**
     * 組裝內(nèi)容范圍的響應(yīng)頭。
     * <pre>
     * <a >
     *     4.2. Content-Range - HTTP/1.1 Range Requests</a>
     * Content-Range: "bytes" first-byte-pos "-" last-byte-pos  "/" complete-length
     *
     * For example:
     * Content-Range: bytes 0-499/1234
     * </pre>
     *
     * @return 內(nèi)容范圍的響應(yīng)頭
     */
    public String toContentRange() {
        return BYTES_STRING + ' ' + start + '-' + end + '/' + length;
//        return "bytes " + start + "-" + end + "/" + length;
    }

    /**
     * 計(jì)算內(nèi)容完整的長(zhǎng)度/總長(zhǎng)度。
     *
     * @return 內(nèi)容完整的長(zhǎng)度/總長(zhǎng)度
     */
    public long applyAsContentLength() {
        return end - start + 1;
    }

    /**
     * Validate range.
     *
     * @return true if the range is valid, otherwise false
     */
    public boolean validate() {
        if (end >= length) {
            end = length - 1;
        }
        return (start >= 0) && (end >= 0) && (start <= end) && (length > 0);
    }

    @Override
    public String toString() {
        return "firstBytePos=" + start +
                ", lastBytePos=" + end +
                ", fileLength=" + length;
    }
}

StreamProgressImpl

package com.example.insurance.common;

import cn.hutool.core.io.StreamProgress;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 數(shù)據(jù)流進(jìn)度條
 */
@Slf4j
@AllArgsConstructor
public class StreamProgressImpl implements StreamProgress {

    private final String fileName;

    @Override
    public void start() {
        log.info("start progress {}", fileName);
    }

    @Override
    public void progress(long total, long progressSize) {
        log.debug("progress {}, total={}, progressSize={}", fileName, total, progressSize);
    }

    @Override
    public void finish() {
        log.info("finish progress {}", fileName);
    }
}

MediaContentUtil

package com.example.insurance.common;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * 文件內(nèi)容輔助方法集
 */
public final class MediaContentUtil {

    public static String filePath() {
        String osName = System.getProperty("os.name");
        String filePath = "/data/files/";
        if (osName.startsWith("Windows")) {
            filePath = "D:\" + filePath;
        }
//        else if (osName.startsWith("Linux")) {
//            filePath = MediaContentConstant.FILE_PATH;
//        }
        else if (osName.startsWith("Mac") || osName.startsWith("Linux")) {
            filePath = "/home/admin" + filePath;
        }
        return filePath;
    }

    public static String encode(String fileName) {
        return URLEncoder.encode(fileName, StandardCharsets.UTF_8);
    }

    public static String decode(String fileName) {
        return URLDecoder.decode(fileName, StandardCharsets.UTF_8);
    }
}

到了這里,關(guān)于Spring-Boot實(shí)現(xiàn)HTTP大文件斷點(diǎn)續(xù)傳分片下載-大視頻分段漸進(jìn)式播放的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • JAVA面試題分享五百一十一:Spring Boot基于WebUploader實(shí)現(xiàn)超大文件上傳和斷點(diǎn)續(xù)傳

    JAVA面試題分享五百一十一:Spring Boot基于WebUploader實(shí)現(xiàn)超大文件上傳和斷點(diǎn)續(xù)傳

    目錄 前言 目標(biāo) 實(shí)現(xiàn)思路 大文件分片 合并分片 斷點(diǎn)續(xù)傳 代碼實(shí)現(xiàn) 1、webuploader組件中,分片上傳怎么開啟? 2、webuploader組件中,文件的md5值如何計(jì)算? 3、webuploader組件中,分片文件的md5值如何計(jì)算? 4、webuploader組件中,分片上傳的的請(qǐng)求在哪里觸發(fā)? 5、前端、后端如何校

    2024年02月19日
    瀏覽(28)
  • spring-boot中實(shí)現(xiàn)分片上傳文件

    spring-boot中實(shí)現(xiàn)分片上傳文件

    一、上傳文件基本實(shí)現(xiàn) 1、前端效果圖展示,這里使用 element-ui plus 來展示樣式效果 2、基礎(chǔ)代碼如下 3、定義后端接口,并且處理好跨域(關(guān)于跨域處理,自己百度處理) 4、保存文件到本地文件 二、配置靜態(tài)目錄 1、在 FileApplication.java 旁邊添加一個(gè) SpringMvcConfig.java 的文件 2、直

    2024年02月05日
    瀏覽(26)
  • 【SpringBoot整合系列】SpringBoot 實(shí)現(xiàn)大文件分片上傳、斷點(diǎn)續(xù)傳及秒傳

    【SpringBoot整合系列】SpringBoot 實(shí)現(xiàn)大文件分片上傳、斷點(diǎn)續(xù)傳及秒傳

    小文件(圖片、文檔、視頻)上傳可以直接使用很多ui框架封裝的上傳組件,或者自己寫一個(gè)input 上傳,利用FormData 對(duì)象提交文件數(shù)據(jù),后端使用spring提供的MultipartFile進(jìn)行文件的接收,然后寫入即可。 但是對(duì)于比較大的文件,比如上傳2G左右的文件(http上傳),就需要將文件

    2024年04月16日
    瀏覽(37)
  • minio&前后端分離上傳視頻/上傳大文件——前后端分離斷點(diǎn)續(xù)傳&minio分片上傳實(shí)現(xiàn)

    minio&前后端分離上傳視頻/上傳大文件——前后端分離斷點(diǎn)續(xù)傳&minio分片上傳實(shí)現(xiàn)

    ????????分布式文件系統(tǒng)-minio: 第一章:分布式文件系統(tǒng)介紹與minio介紹與使用(附minio java client 使用) 第二章:minio前后端分離上傳視頻/上傳大文件——前后端分離斷點(diǎn)續(xù)傳minio分片上傳實(shí)現(xiàn) 斷點(diǎn)續(xù)傳指的是在下載或上傳時(shí),將下載或上傳任務(wù)(一個(gè)文件或一個(gè)壓縮包

    2024年02月03日
    瀏覽(44)
  • 前端文件上傳(文件上傳,分片上傳,斷點(diǎn)續(xù)傳)

    前端文件上傳(文件上傳,分片上傳,斷點(diǎn)續(xù)傳)

    普通文件上傳 思路: 首先獲取用戶選擇的文件對(duì)象,并將其添加到一個(gè) FormData 對(duì)象中。然后,使用 axios 的 post 方法將 FormData 對(duì)象發(fā)送到服務(wù)器。在 then 和 catch 中,我們分別處理上傳成功和失敗的情況,并輸出相應(yīng)的信息。 需要注意,在使用 axios 進(jìn)行文件上傳時(shí),必須將

    2024年02月22日
    瀏覽(30)
  • 【萬字長(zhǎng)文】Vue+SpringBoot實(shí)現(xiàn)大文件秒傳、斷點(diǎn)續(xù)傳和分片上傳完整教程(提供Gitee源碼)

    前言:最近在實(shí)際項(xiàng)目中碰到一個(gè)需求,客戶可能會(huì)上傳比較大的文件,如果采用傳統(tǒng)的文件上傳方案可能會(huì)存在服務(wù)器壓力大、資源浪費(fèi)甚至內(nèi)存溢出的一些安全風(fēng)險(xiǎn),所以為了解決一系列問題,需要采用新的技術(shù)方案來實(shí)現(xiàn)大文件的上傳;空閑的時(shí)候參考了網(wǎng)上的一些相

    2024年02月12日
    瀏覽(24)
  • lazarus、delphi文件Http下載斷點(diǎn)續(xù)傳的實(shí)現(xiàn)

    下載大文件時(shí),斷點(diǎn)續(xù)傳是很有必要的,特別是網(wǎng)速度慢且不穩(wěn)定的情況下,很難保證不出意外,一旦意外中斷,又要從頭下載,會(huì)很讓人抓狂。斷點(diǎn)續(xù)傳就能很好解決意外中斷情況,再次下載時(shí)不需要從頭下載,從上次中斷處繼續(xù)下載即可,這樣下載幾G或十幾G大小的一個(gè)

    2024年02月13日
    瀏覽(34)
  • 大文件上傳阿里云oss,分片、斷點(diǎn)續(xù)傳進(jìn)度條展示

    大文件上傳阿里云oss,分片、斷點(diǎn)續(xù)傳進(jìn)度條展示

    前端頁面展示 大文件如果不采用分片上傳會(huì)導(dǎo)致卡死、內(nèi)存占用過高導(dǎo)致程序奔潰等一些列問題。 通常在文件大于100 MB的情況下,建議采用分片上傳的方法,通過斷點(diǎn)續(xù)傳和重試,提高上傳成功率。如果在文件小于100 MB的情況下使用分片上傳,且partSize設(shè)置不合理的情況下,

    2024年02月11日
    瀏覽(26)
  • 前端 + 后端 實(shí)現(xiàn)分片上傳(斷點(diǎn)續(xù)傳/極速秒傳)

    先記錄下,后面有時(shí)間再去實(shí)現(xiàn) (已實(shí)現(xiàn),可參考 SpringBoot+vue文件上傳下載預(yù)覽大文件分片上傳文件上傳進(jìn)度 SpringBoot+vue 大文件分片下載) 可參考鏈接:vue上傳大文件/視頻前后端(java)代碼 前端slice分片上傳,后端用表記錄分片索引和分片大小和分片總數(shù),當(dāng)接受完最后

    2023年04月17日
    瀏覽(25)
  • SpringBoot整合minio實(shí)現(xiàn)斷點(diǎn)續(xù)傳、分片上傳(附源碼)

    SpringBoot整合minio實(shí)現(xiàn)斷點(diǎn)續(xù)傳、分片上傳(附源碼)

    在Web開發(fā)中,大文件的上傳是必不可少的功能之一。本文將介紹如何使用SpringBoot整合minio實(shí)現(xiàn)一個(gè)簡(jiǎn)單的大文件上傳網(wǎng)站。 項(xiàng)目下載 gitee:https://gitee.com/wusupweilgy/springboot-vue.git 前端:vue2、element-ui組件、axios 后端:springboot、minio、mybatis-plus、redis 斷點(diǎn)續(xù)傳 分片上傳 前端顯示

    2024年02月04日
    瀏覽(25)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包