• http断点下载客户端和服务端


    客户端;
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;

    public class ArcSyncHttpClient {
    public static void main(String[] args) throws Exception {


    String strURL = "http://localhost:8080/ArcSyncHttpDownload/httpServlet";
    URL downUrl = new URL(strURL);
    HttpURLConnection cn = (HttpURLConnection) downUrl.openConnection();
    if (cn.getResponseCode() == 200) {
    // InputStream is = cn.getInputStream();
    int part_count = 10;

    int contentLength = cn.getContentLength();
    System.out.println("cn.getContentLength():"+cn.getContentLength());
    int firstLength = contentLength / part_count;
    System.out.println("firstLength():" + firstLength);
    cn.disconnect();
    // part_count = 1;
    for (int i = 1; i < part_count+1; i++) {
    if (i==part_count) {
    // int lastLength = contentLength -firstLength*(part_count-1);
    File saveFile = new File("E:/test/123",String.format("123_0%d.mp3", i));
    TestDownload.download(saveFile.getName(), saveFile.getParent(),
    strURL, (i-1)*firstLength, contentLength);
    }else {
    File saveFile = new File("E:/test/123",String.format("123_0%d.mp3", i));
    TestDownload.download(saveFile.getName(), saveFile.getParent(),
    strURL, (i-1)*firstLength, firstLength*i);
    }

    }
    }
    }

    public static void download( String fileName, String filePath,String remoteUrl, long start ,long end) {
    byte[] buf = new byte[10240];
    HttpURLConnection httpURLConnection;
    URL url;
    BufferedInputStream bis;
    int size;
    RandomAccessFile rndFile;
    // 下载文件
    try {
    url = new URL(remoteUrl);
    httpURLConnection = (HttpURLConnection)url.openConnection();
    // 设置User-Agent
    httpURLConnection.setRequestProperty("User-Agent", "Net");
    // 设置续传开始
    httpURLConnection.setRequestProperty("Range", "bytes=" + start + "-"+ end);
    // 获取输入流
    bis = new BufferedInputStream(httpURLConnection.getInputStream());
    rndFile = new RandomAccessFile(filePath + "\" + fileName, "rw");
    rndFile.seek(0);
    int i = 0;
    while ((size = bis.read(buf)) != -1) {
    //if (i > 500) break;
    rndFile.write(buf, 0, size);

    i++;
    }
    System.out.println("i=" + i);
    httpURLConnection.disconnect();
    bis.close();
    rndFile.close();
    } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
    }
    }

    public static long getRemoteFileSzie(String url) {
    long size = 0;
    try {
    HttpURLConnection httpUrl = (HttpURLConnection)(new URL(url)).openConnection();
    size = httpUrl.getContentLength();
    httpUrl.disconnect();
    } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
    }
    return size;
    }

    public static void mergeFiles(String outFile, List<String> files) {
    FileChannel outChannel = null;
    System.out.println("Merge " + files + " into " + outFile);
    try {
    outChannel = new FileOutputStream(outFile).getChannel();
    for (String f : files) {
    FileChannel fc = new FileInputStream(f).getChannel();
    ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);
    while (fc.read(bb) != -1) {
    bb.flip();
    outChannel.write(bb);
    bb.clear();
    }
    fc.close();
    }
    System.out.println("Merged!! ");
    } catch (IOException ioe) {
    ioe.printStackTrace();
    } finally {
    try {
    if (outChannel != null) {
    outChannel.close();
    }
    } catch (IOException ignore) {
    }
    }
    }

    }

    服务端

    package com.defonds.cds.common;


    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.RandomAccessFile;

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

    //HTTP 断点续传 demo(客户端测试工具:快车、迅雷)
    public class ArcSyncHttpDownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    // final static Log log = LogFactory.getLog(ArcSyncHttpDownloadServlet.class);

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
    File downloadFile = new File("E:/test/123.mp3");//要下载的文件
    long fileLength = downloadFile.length();//记录文件大小
    System.out.println("fileLength:"+fileLength);
    long pastLength = 0;//记录已下载文件大小
    int rangeSwitch = 0;//0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000)
    long toLength = 0;//记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000)
    long contentLength = 0;//客户端请求的字节总量
    String rangeBytes = "";//记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容
    RandomAccessFile raf = null;//负责读取数据
    OutputStream os = null;//写出数据
    OutputStream out = null;//缓冲
    byte b[] = new byte[1024];//暂存容器

    if (request.getHeader("Range") != null) {// 客户端请求的下载的文件块的开始字节
    response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
    System.out.println("request.getHeader("Range")=" + request.getHeader("Range"));
    rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");
    int index = rangeBytes.indexOf('-');
    int length = rangeBytes.length()-1;
    if (index == 1) {//bytes=969998336-
    rangeSwitch = 1;
    // rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
    String temp0 = rangeBytes.substring(0,rangeBytes.indexOf('-'));
    String temp2 = rangeBytes.substring(rangeBytes.indexOf('-') + 1, rangeBytes.length());
    pastLength = Long.parseLong(temp0.trim());
    if ("".equals(temp2)) {
    contentLength = fileLength - pastLength + 1;//客户端请求的是 969998336 之后的字节
    }else {
    toLength = Long.parseLong(temp2);//bytes=1275856879-1275877358,到第 1275877358 个字节结束
    contentLength = toLength - pastLength + 1;//客户端请求的是 1275856879-1275877358 之间的字节
    }
    } else {//bytes=1275856879-1275877358
    rangeSwitch = 2;
    String temp0 = rangeBytes.substring(0,rangeBytes.indexOf('-'));
    String temp2 = rangeBytes.substring(rangeBytes.indexOf('-') + 1, rangeBytes.length());
    pastLength = Long.parseLong(temp0.trim());//bytes=1275856879-1275877358,从第 1275856879 个字节开始下载
    toLength = Long.parseLong(temp2);//bytes=1275856879-1275877358,到第 1275877358 个字节结束
    contentLength = toLength - pastLength + 1;//客户端请求的是 1275856879-1275877358 之间的字节
    }
    } else {//从开始进行下载
    contentLength = fileLength;//客户端要求全文下载
    }

    /**
    * 如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。
    * 响应的格式是:
    * Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
    * ServletActionContext.getResponse().setHeader("Content-Length",
    * new Long(file.length() - p).toString());
    */
    response.reset();//告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes
    response.setHeader("Accept-Ranges", "bytes");//如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK
    if (pastLength != 0) {
    //不是从最开始下载,
    //响应的格式是:
    //Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
    System.out.println("----------------------------不是从开始进行下载!服务器即将开始断点续传...");
    switch (rangeSwitch) {
    case 1 : {//针对 bytes=27000- 的请求
    String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-").append(new Long(fileLength - 1).toString()).append("/").append(new Long(fileLength).toString()).toString();
    response.setHeader("Content-Range", contentRange);
    break;
    }
    case 2 : {//针对 bytes=27000-39000 的请求
    String contentRange = rangeBytes + "/" + new Long(fileLength).toString();
    response.setHeader("Content-Range", contentRange);
    break;
    }
    default : {
    break;
    }
    }
    } else {
    //是从开始下载
    System.out.println("----------------------------是从开始进行下载!");
    }

    try {
    response.addHeader("Content-Disposition", "attachment; filename="" + downloadFile.getName() + """);
    response.setContentType( CommonUtil.setContentType(downloadFile.getName()));// set the MIME type.
    response.addHeader("Content-Length", String.valueOf(contentLength));
    os = response.getOutputStream();
    out = new BufferedOutputStream(os);
    raf = new RandomAccessFile(downloadFile, "r");
    try {
    switch (rangeSwitch) {
    case 0 : {//普通下载,或者从头开始的下载
    //同1
    }
    case 1 : {//针对 bytes=27000- 的请求
    raf.seek(pastLength);//形如 bytes=969998336- 的客户端请求,跳过 969998336 个字节
    int n = 0;
    while ((n = raf.read(b, 0, 1024)) != -1) {
    out.write(b, 0, n);
    }
    break;
    }
    case 2 : {//针对 bytes=27000-39000 的请求
    raf.seek(pastLength - 1);//形如 bytes=1275856879-1275877358 的客户端请求,找到第 1275856879 个字节
    int n = 0;
    long readLength = 0;//记录已读字节数
    while (readLength <= contentLength - 1024) {//大部分字节在这里读取
    n = raf.read(b, 0, 1024);
    readLength += 1024;
    out.write(b, 0, n);
    }
    if (readLength <= contentLength) {//余下的不足 1024 个字节在这里读取
    n = raf.read(b, 0, (int)(contentLength - readLength));
    out.write(b, 0, n);
    }
    //
    // raf.seek(pastLength);//形如 bytes=1275856879-1275877358 的客户端请求,找到第 1275856879 个字节
    // while (raf.getFilePointer() < toLength) {
    // out.write(raf.read());
    // }
    break;
    }
    default : {
    break;
    }
    }
    out.flush();
    } catch(IOException ie) {
    /**
    * 在写数据的时候,
    * 对于 ClientAbortException 之类的异常,
    * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时,
    * 抛出这个异常,这个是正常的。
    * 尤其是对于迅雷这种吸血的客户端软件,
    * 明明已经有一个线程在读取 bytes=1275856879-1275877358,
    * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段,
    * 直到有一个线程读取完毕,迅雷会 KILL 掉其他正在下载同一字节段的线程,
    * 强行中止字节读出,造成服务器抛 ClientAbortException。
    * 所以,我们忽略这种异常
    */
    //ignore
    }
    } catch (Exception e) {
    System.err.println( e);
    } finally {
    if (out != null) {
    try {
    out.close();
    } catch (IOException e) {
    System.err.println( e);
    }
    }
    if (raf != null) {
    try {
    raf.close();
    } catch (IOException e) {
    System.err.println( e);
    }
    }
    }
    }
    }

    package com.defonds.cds.common;
    public class CommonUtil {

    public static String setContentType(String returnFileName) {
    String contentType = "application/octet-stream";
    if (returnFileName.lastIndexOf(".") < 0)
    return contentType;
    returnFileName = returnFileName.toLowerCase();
    returnFileName = returnFileName.substring(returnFileName
    .lastIndexOf(".") + 1);

    if (returnFileName.equals("html") || returnFileName.equals("htm")
    || returnFileName.equals("shtml")) {
    contentType = "text/html";
    } else if (returnFileName.equals("css")) {
    contentType = "text/css";
    } else if (returnFileName.equals("xml")) {
    contentType = "text/xml";
    } else if (returnFileName.equals("gif")) {
    contentType = "image/gif";
    } else if (returnFileName.equals("jpeg")
    || returnFileName.equals("jpg")) {
    contentType = "image/jpeg";
    } else if (returnFileName.equals("js")) {
    contentType = "application/x-javascript";
    } else if (returnFileName.equals("atom")) {
    contentType = "application/atom+xml";
    } else if (returnFileName.equals("rss")) {
    contentType = "application/rss+xml";
    } else if (returnFileName.equals("mml")) {
    contentType = "text/mathml";
    } else if (returnFileName.equals("txt")) {
    contentType = "text/plain";
    } else if (returnFileName.equals("jad")) {
    contentType = "text/vnd.sun.j2me.app-descriptor";
    } else if (returnFileName.equals("wml")) {
    contentType = "text/vnd.wap.wml";
    } else if (returnFileName.equals("htc")) {
    contentType = "text/x-component";
    } else if (returnFileName.equals("png")) {
    contentType = "image/png";
    } else if (returnFileName.equals("tif")
    || returnFileName.equals("tiff")) {
    contentType = "image/tiff";
    } else if (returnFileName.equals("wbmp")) {
    contentType = "image/vnd.wap.wbmp";
    } else if (returnFileName.equals("ico")) {
    contentType = "image/x-icon";
    } else if (returnFileName.equals("jng")) {
    contentType = "image/x-jng";
    } else if (returnFileName.equals("bmp")) {
    contentType = "image/x-ms-bmp";
    } else if (returnFileName.equals("svg")) {
    contentType = "image/svg+xml";
    } else if (returnFileName.equals("jar") || returnFileName.equals("var")
    || returnFileName.equals("ear")) {
    contentType = "application/java-archive";
    } else if (returnFileName.equals("doc")) {
    contentType = "application/msword";
    } else if (returnFileName.equals("pdf")) {
    contentType = "application/pdf";
    } else if (returnFileName.equals("rtf")) {
    contentType = "application/rtf";
    } else if (returnFileName.equals("xls")) {
    contentType = "application/vnd.ms-excel";
    } else if (returnFileName.equals("ppt")) {
    contentType = "application/vnd.ms-powerpoint";
    } else if (returnFileName.equals("7z")) {
    contentType = "application/x-7z-compressed";
    } else if (returnFileName.equals("rar")) {
    contentType = "application/x-rar-compressed";
    } else if (returnFileName.equals("swf")) {
    contentType = "application/x-shockwave-flash";
    } else if (returnFileName.equals("rpm")) {
    contentType = "application/x-redhat-package-manager";
    } else if (returnFileName.equals("der") || returnFileName.equals("pem")
    || returnFileName.equals("crt")) {
    contentType = "application/x-x509-ca-cert";
    } else if (returnFileName.equals("xhtml")) {
    contentType = "application/xhtml+xml";
    } else if (returnFileName.equals("zip")) {
    contentType = "application/zip";
    } else if (returnFileName.equals("mid")
    || returnFileName.equals("midi")
    || returnFileName.equals("kar")) {
    contentType = "audio/midi";
    } else if (returnFileName.equals("mp3")) {
    contentType = "audio/mpeg";
    } else if (returnFileName.equals("ogg")) {
    contentType = "audio/ogg";
    } else if (returnFileName.equals("m4a")) {
    contentType = "audio/x-m4a";
    } else if (returnFileName.equals("ra")) {
    contentType = "audio/x-realaudio";
    } else if (returnFileName.equals("3gpp")
    || returnFileName.equals("3gp")) {
    contentType = "video/3gpp";
    } else if (returnFileName.equals("mp4")) {
    contentType = "video/mp4";
    } else if (returnFileName.equals("mpeg")
    || returnFileName.equals("mpg")) {
    contentType = "video/mpeg";
    } else if (returnFileName.equals("mov")) {
    contentType = "video/quicktime";
    } else if (returnFileName.equals("flv")) {
    contentType = "video/x-flv";
    } else if (returnFileName.equals("m4v")) {
    contentType = "video/x-m4v";
    } else if (returnFileName.equals("mng")) {
    contentType = "video/x-mng";
    } else if (returnFileName.equals("asx") || returnFileName.equals("asf")) {
    contentType = "video/x-ms-asf";
    } else if (returnFileName.equals("wmv")) {
    contentType = "video/x-ms-wmv";
    } else if (returnFileName.equals("avi")) {
    contentType = "video/x-msvideo";
    }

    return contentType;
    }
    }

  • 相关阅读:
    关于JDK中的设计模式
    关于Java中的构造方法和set方法()给属性赋值
    关于Object[]数组强转成Integer[]类型的数组.
    [ImportNew]Java中的Timer类和TimerTask类
    关于Linux最基本总结
    关于数组
    关于Linux系统和Windows系统中文件夹的命名规范
    关于数组集合之间的转换
    关于Java获取系统信息
    关于Windows常用命令
  • 原文地址:https://www.cnblogs.com/shortboy/p/4082606.html
Copyright © 2020-2023  润新知