Maison >Java >javaDidacticiel >Exemple de partage de l'implémentation Java du principe de téléchargement de la reprise du point d'arrêt

Exemple de partage de l'implémentation Java du principe de téléchargement de la reprise du point d'arrêt

黄舟
黄舟original
2017-09-06 09:54:092031parcourir

Contexte requis

  • Lors du téléchargement de fichiers créés dynamiquement, j'espère que le navigateur affichera la progression du téléchargement

  • Les fichiers créés dynamiquement espèrent que ils peuvent être segmentés Télécharger

Message de transmission HTTP avec reprise

Pour mettre en œuvre la HTTPtransmission avec reprise, vous devez comprendre brièvement les messages suivants.

  • Accept-Ranges indique au client (navigateur..) que le serveur prend en charge la reprise du point d'arrêt 服务器端返回

  • Le client Range indique Les téléchargements du serveur ressources de l'emplacement/plage spécifié (le nombre d'octets ici) 客户端发出

  • Content-Range Le serveur indique au client les informations sur les données de réponse, dans tout le corps du retour La position de l'octet de cette partie 服务器端返回

  • Identificateur de ressource ETag 非必须 服务器端返回

  • Dernière mise à jour de la ressource Dernière modification Le format de plage de temps 非必须 服务器端返回

Range

  1. représente la plage de 0 à 499 octets : Plage : octets =0 à 499

  2. représente la plage des 500 derniers octets : Range : bytes=-500

  3. représente la plage du début à la fin des 500 octets : Range : bytes=500-

  4. représente le premier et le dernier octets : Plage : bytes=0-0,-1

  5. Indique que plusieurs plages sont spécifiés en même temps : Plage : octets=500-600,601-999

Content-Range format de données

Content-Range : octets 0-499/ 22036 : Indique qu'un total de 22 036 octets de ressources de données comprises entre 0 et 499 octets sont renvoyés

Principe

  1. Le client lance un paramètre de requêteRangeSpécifie le nombre d'octets de départ ou Il n'est pas nécessaire de définir le numéro d'octet de fin s'il commence à 0.

  2. Le serveur vérifie que l'en-tête Range du client analyse le numéro d'octet de début et le numéro d'octet de fin et renvoie l'en-tête du message Accept-Ranges indiquant qu'il prend en charge la reprise du point d'arrêt, Content-Range Enregistrement les informations d'emplacement du flux écrit sur le client, puis écrire le flux sur le client.

  3. Le serveur peut utiliser ETag Last-Modified pour marquer si la ressource a été modifiée. Effectuez un travail de vérification et renvoyez une erreur si la vérification échoue, ce qui n'est pas obligatoire.

Implémentation Java

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){}
        }
    }
}

Par exemple, lors du téléchargement de Google Chrome, vous pouvez voir la progression du téléchargement, suspendre le téléchargement et reprendre les opérations de téléchargement, et vous pouvez également définir Téléchargement du segment des points de test de plage.


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn