基于Range协议的文件下载

当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;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值