基于Range协议的文件下载

原创
2016/03/31 17:52
阅读数 91

当http请求头中包含

Accept-Ranges: bytes

时,若响应头为

HTTP/1.1 206 Partial Content

则代表该资源支持切片下载。只需要在请求头中加入

Range: bytes=start-end

服务端就会响应给定的范围内的资源。

HTTP Range文件下载示例:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
 
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
 
import com.jfinal.plugin.ehcache.CacheKit;
import com.sdzn.iload.model.FileBean;
import com.sdzn.iload.model.ResultEnum;
 
public class HttpClient {
 
    private static final Logger log = Logger.getLogger(HttpClient.class);
    private static RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000).build();
 
    /**
     * 获取文件下载信息
     * 
     * @param fb
     */
    public static void getFileOption(FileBean fb) {
        HttpGet httpget = new HttpGet(fb.getFileUrl());
        httpget.setConfig(requestConfig);
        CloseableHttpResponse response = null;
        CloseableHttpClient httpclient = null;
        try {
            httpclient = HttpClients.createDefault();
            response = httpclient.execute(httpget);
            if (response.getStatusLine().getStatusCode() == 200) {
                fb.setContentLength(Long.parseLong(response.getFirstHeader(
                        "Content-Length").getValue()));
                fb.setStart(0l);
                fb.setEnd(fb.getContentLength());
                fb.seteTag(response.getFirstHeader("ETag").getValue());
                System.out.println(response.getFirstHeader("Accept-Ranges")
                        .getValue());
                if (response.getFirstHeader("Accept-Ranges").getValue()
                        .equalsIgnoreCase("bytes")) {
                    fb.setRange(true);
                }
                fb.setResult(ResultEnum.GET_INFO_SUCCESS.getValue());
            } else {
                fb.setResult(ResultEnum.GET_INFO_FAIL.getValue());
            }
        } catch (Exception e) {
            log.error("获取下载信息失败", e);
            fb.setResult(ResultEnum.GET_INFO_FAIL.getValue());
        } finally {
            closeHttpClient(httpclient);
            closeHttpResponse(response);
        }
    }
 
    /**
     * 支持断点的文件下载
     * 
     * @param fb
     */
    public static void getRangeFile(FileBean fb) {
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        Path savePath = Paths.get(fb.getSaveUrl());
        Path fileName = savePath.getFileName();
        String tempName = StringUtil.replaceSuffix(fileName.toString(), "tmp");
        // 临时文件
        Path tempPath = Paths.get(savePath.getParent().toString(), tempName);
        fb.setTempPath(tempPath.toString());
        try {
            if (!Files.exists(tempPath)) {
                tempPath = createFile(tempPath);
            }
            httpclient = HttpClients.createDefault();
            HttpGet httpget = new HttpGet(fb.getFileUrl());
            httpget.setConfig(requestConfig);
            StringBuilder sb = new StringBuilder();
            sb.append("bytes=").append(fb.getStart()).append("-")
                    .append(fb.getEnd());
            httpget.setHeader("Range", sb.toString());
            response = httpclient.execute(httpget);
            entity = response.getEntity();
            if ((response.getStatusLine().getStatusCode() == 200 || response
                    .getStatusLine().getStatusCode() == 206)
                    && entity.getContentLength() > 0
                    && response.getFirstHeader("ETag").getValue()
                            .equals(fb.geteTag())) {
                try (FileChannel fc = FileChannel.open(tempPath,
                        StandardOpenOption.WRITE, StandardOpenOption.SYNC);
                        InputStream is = entity.getContent();) {
                    fc.position(fb.getStart());
                    // fc.force(true);
                    byte[] read = new byte[8192];
                    int len = is.read(read);
                    while (len > 0) {
                        fc.write(ByteBuffer.wrap(read, 0, len));
                        fb.addRateLength((long) len);
                        if (fb.isCut()) {
                            FileBean p = CacheKit.get("FILEBEAN",
                                    fb.getParentTaskId());
                            if (p != null) {
                                p.addRateLength((long) len);
                                p = CacheKit.get("FILEBEAN",
                                        fb.getParentTaskId());
                            }
                        }
                        if (!fb.getRunStatus()) {
                            len = is.read(read);
                        } else {
                            len = 0;
                        }
                    }
                }
                log.info("rate:" + fb.getRateLength() + ";len:"
                        + fb.getContentLength());
                if (fb.getRunStatus()) {
                    fb.setResult(ResultEnum.STOP.getValue());
                } else if (fb.getRateLength().equals(fb.getContentLength())) {
                    fb.setResult(ResultEnum.SUCCESS.getValue());
                } else {
                    fb.setResult(ResultEnum.FAIL.getValue());
                }
            } else {
                fb.setResult(ResultEnum.NOTFEXP.getValue());
            }
        } catch (Exception e) {
            log.error("下载文件失败!", e);
            fb.setResult(ResultEnum.EXP.getValue());
        } finally {
            closeHttpClient(httpclient);
            closeHttpResponse(response);
            consumeEntity(entity);
        }
    }
 
    /**
     * 下载任务
     * 
     * @param fb
     */
    public static void getFile(FileBean fb) {
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        Path savePath = Paths.get(fb.getSaveUrl());
        Path fileName = savePath.getFileName();
        String tempName = StringUtil.replaceSuffix(fileName.toString(), "tmp");
        // 下载临时文件
        Path tempPath = Paths.get(savePath.getParent().toString(), tempName);
        try {
            if (!Files.exists(tempPath)) {
                tempPath = Files.createFile(tempPath);
            }
            httpclient = HttpClients.createDefault();
            HttpGet httpget = new HttpGet(fb.getFileUrl());
            httpget.setConfig(requestConfig);
            response = httpclient.execute(httpget);
            entity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200
                    && entity.getContentLength() > 0
                    && response.getFirstHeader("ETag").getValue()
                            .equals(fb.geteTag())) {
                try (InputStream in = entity.getContent();
                        OutputStream os = Files.newOutputStream(tempPath);) {
                    byte[] read = new byte[8192];
                    int length = in.read(read);
                    while (length > 0) {
                        os.write(read, 0, length);
                        fb.addRateLength((long) length);
                        if (!fb.getRunStatus()) {
                            length = in.read(read);
                        } else {
                            length = 0;
                            fb.setResult(ResultEnum.STOP.getValue());
                        }
                    }
                }
                if (fb.getRateLength().equals(fb.getContentLength())) {
                    fb.setResult(ResultEnum.SUCCESS.getValue());
                    // 修改临时文件名
                    tempPath.toFile().renameTo(savePath.toFile());
                } else {
                    fb.setResult(ResultEnum.FAIL.getValue());
                }
            } else {
                fb.setResult(ResultEnum.NOTFEXP.getValue());
            }
        } catch (Exception e) {
            log.error("下载文件失败!", e);
            fb.setResult(ResultEnum.IOEXP.getValue());
        } finally {
            closeHttpClient(httpclient);
            closeHttpResponse(response);
            consumeEntity(entity);
            if (!fb.getResult().equals(ResultEnum.SUCCESS.getValue())) {
                deletePathIfExists(tempPath);
            }
        }
    }
 
    private static void closeHttpClient(CloseableHttpClient client) {
        if (client != null) try {
            client.close();
        } catch (IOException e) {
        }
    }
 
    private static void closeHttpResponse(CloseableHttpResponse response) {
        if (response != null) try {
            response.close();
        } catch (IOException e) {
        }
    }
 
    private static void consumeEntity(HttpEntity entity) {
        if (entity != null) try {
            EntityUtils.consume(entity);
        } catch (IOException e) {
        }
    }
 
    private static void deletePathIfExists(Path path) {
        try {
            Files.deleteIfExists(path);
        } catch (IOException e) {
        }
    }
 
    private static synchronized Path createFile(Path path) throws IOException {
        if (!Files.exists(path)) {
            return Files.createFile(path);
        }
        return path;
    }
 
 
}


展开阅读全文
打赏
1
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部