Home  >  Article  >  Java  >  Example sharing of Java implementation of breakpoint resume download principle

Example sharing of Java implementation of breakpoint resume download principle

黄舟
黄舟Original
2017-09-06 09:54:091974browse

Requirement background

  • When downloading dynamically created files, I hope the browser will display the download progress

  • I hope the dynamically created files can be segmented Download

HTTP resumable transmission message

To implement HTTPresumable transmission, you must briefly understand the following messages.

  • Accept-Ranges tells the client (browser..) that the server supports breakpoint resumption The server returns

  • Range The client tells the server to download resources from the specified location/range (number of bytes here) The client issues

  • Content-Range server Tell the client the response data information, the byte position of this part in the entire return body The server returns

  • ETag resource identifier Not required Server-side return

  • Last-Modified resource last updated time Not required Server-side return## The range format of

#Range

  1. represents the range of 0-499 bytes: Range: bytes=0-499

  2. represents the range of the last 500 bytes: Range: bytes=-500

  3. represents the range from the beginning to the end of 500 bytes: Range: bytes=500 -

  4. means the first and last bytes: Range: bytes=0-0,-1

  5. # means specifying several at the same time Range: Range: bytes=500-600,601-999

Content-Range data format

Content-Range: bytes 0-499/ 22036: Indicates that a total of 22036 bytes of data resources in the range of 0-499 bytes are returned

Principle

  1. The client initiates a request setting

    RangeSpecify the start The number of bytes or the number of end bytes does not need to be set if it starts from 0.

  2. The server checks that the client

    Range header parses the starting byte number and ending byte number and returns the message header Accept-Ranges Indicates that breakpoint resumption is supported, Content-Range records the location information of the stream written to the client, and then writes the stream to the client.

  3. The server can use

    ETag Last-Modified to mark whether the resource has been modified. Do some verification work, and return an error if the verification fails, which is not required.

java implementation

OutputStream os=null;
    InputStream inputStream =null;
    File zipFile=null;
    try{
        long zipStart=System.currentTimeMillis();
        zipFile=createFile();//动态根据业务创建文件
        if(logger.isInfoEnabled()){
            logger.info(String.format("压缩ZIP 花费时间 %s(s) ",
        (System.currentTimeMillis()-zipStart)/1000));
        }
        if (zipFile.exists()) {
            long downloadStart=System.currentTimeMillis();
            inputStream= new BufferedInputStream(new FileInputStream(zipFile));
            response.reset();
            os=new BufferedOutputStream(response.getOutputStream());
            String userAgent = request.getHeader("USER-AGENT");
            String fileName=zipFile.getName();
            if (null != userAgent && -1 != userAgent.indexOf("MSIE")) {
                fileName = URLEncoder.encode(fileName, "UTF8");
            } else if (null != userAgent && -1 != userAgent.indexOf("Mozilla")) {
                fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
            }
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-Disposition", 
        "attachment;filename="+ fileName);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            long pos = 0, fileSize=zipFile.length(),
    last=fileSize-1;
            response.setHeader("ETag",zipFile.getName().
         concat(Objects.toString(fileSize))
                    .concat("_").concat(Objects.toString(zipFile.lastModified())));
            response.setDateHeader("Last-Modified",zipFile.lastModified());
            response.setDateHeader("Expires",
            System.currentTimeMillis()+1000*60*60*24);
            if (null != request.getHeader("Range")) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                try {
                    // 暂时只处理这2种range格式 1、RANGE: bytes=111- 2、Range: bytes=0-499
                    String numRang = request.getHeader("Range")
            .replaceAll("bytes=", "");
                    String[] strRange = numRang.split("-");
                    if (strRange.length == 2) {
                        pos = Long.parseLong(strRange[0].trim());
                        last = Long.parseLong(strRange[1].trim());
                    } else {
                        pos = Long.parseLong(numRang.replaceAll("-", "").trim());
                    }
                } catch (NumberFormatException e) {
                    logger.error(request.getHeader("Range") + " error");
                    pos = 0;
                }
            }
            long rangLength = last - pos + 1;
            String contentRange = new StringBuffer("bytes ").
            append(String.valueOf(pos)).
            append("-").append(last).append("/").
            append(String.valueOf(fileSize)).toString();
            response.setHeader("Content-Range", contentRange);
            response.addHeader("Content-Length",Objects.toString(rangLength));
            if(pos>0){
                inputStream.skip(pos);
            }
            byte[] buffer = new byte[1024*512];//每次以512KB 0.5MB的流量下载
            int length = 0,sendTotal=0;
            while (sendTotal < rangLength && length!=-1) {
                length = inputStream.read(buffer, 0,
        ((rangLength - sendTotal) <= buffer.length ?
                        ((int) (rangLength - sendTotal)) : buffer.length));
                sendTotal = sendTotal + length;
                os.write(buffer, 0, length);
            }
            if(os!=null){
                os.flush();
            }
            if(logger.isInfoEnabled()){
                logger.info(String.format("下载 花费时间 %s(s) ",
        (System.currentTimeMillis()-downloadStart)/1000));
            }
        }
    }catch (Exception e){
        if(StringUtils.endsWithIgnoreCase(e.getMessage(),"Broken pipe")){
            logger.error("用户取消下载");
        }
        logger.error(e.getMessage(),e);
    }finally {
        if(os!=null){
            try{
                os.close();
            }catch (Exception e){}
        }
        if(inputStream!=null){
            try{
                IOUtils.closeQuietly(inputStream);
            }catch (Exception e){}
        }
    }
}

For example, when downloading with Google Chrome, you can see the download progress, pause downloading and resume downloading operations, and you can also set Range to test segmented downloads. .


The above is the detailed content of Example sharing of Java implementation of breakpoint resume download principle. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn