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