import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HttpDownloadUtil {
public static void doDownload(HttpServletRequest request,
HttpServletResponse response, String originalName,
String fullFilePath,boolean isEncrypted) throws ServletException, IOException{
doDownload(request,response,originalName,fullFilePath,null,isEncrypted);
}
public static void doDownload(HttpServletRequest request,
HttpServletResponse response, String originalName,
String fullFilePath,String contentType,boolean isEncrypted) throws IOException {
File file = new File(fullFilePath);
if (file.isDirectory() || !file.exists()) {
//得到此次请求的发起路径,并重新回到此路径
String url=request.getHeader("Referer");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
try{
response.getWriter().write("<script>top.$.jBox.tip('文件已丢失或不存在!','error');location.href='"+url+"'</script>");
} catch (IOException e) {
e.printStackTrace();
log.error("因文件已丢失或不存在导致打开文件失败时在返回出错信息时出现异常. ", e.getMessage());
throw e;
}
return;
}
try {
// 文件名
String filename = originalName;
// 清空缓存
response.reset();
//response.setHeader("Server", "www.xx.com");
// 告诉客户端允许断点续传多线程连接下载
// 响应的格式是如下:
// Accept-Ranges: bytes
response.setHeader("Accept-Ranges", "bytes");
// 文件长度
long mLength = 0;
mLength = file.length();
if (isEncrypted) {
mLength -= 20;
}
// 请求文件的开始
long start = 0;
// 结束位置
long end = mLength - 1;
// 如果是第一次下,还没有断点续传,状态是默认的 200,无需设置
// 响应的格式是: HTTP/1.1 200 OK
if (request.getHeader("Range") != null) {
// 客户端请求的下载的文件块的开始字节和结束
// 如果是下载文件的范围而不是全部,向客户端声明支持并开始文件块下载
// 要设置状态
// 响应的格式是: HTTP/1.1 206 Partial Content
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);// 206
// 从请求中得到开始的字节
// 请求的格式是:
// Range: bytes=[文件块的开始字节]-
String[] tokens = request.getHeader("Range")
.replaceAll("bytes=", "").split("-");
start = Long.parseLong(tokens[0]);
if (tokens.length > 1) {
end = Long.parseLong(tokens[1]);
}
}
// 下载的文件(或块)长度
// 响应的格式是:
// Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
response.setHeader("Content-Length",
new Long(end - start + 1).toString());
// if (start > 0) {
// // 不是从最开始下载,
// // 响应的格式是:
// // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
response.setHeader("Content-Range", "bytes " + start + "-"
+ end + "/" + mLength);
// }
if(contentType==null)
response.setContentType("application/octet-stream");
else
response.setContentType(contentType);
// 文件名用ISO08859_1编码
response.setHeader("Content-Disposition", "attachment; filename=\""
+ URLEncoder.encode(filename ,StandardCharsets.UTF_8.name())+ "\"");
// 输出流
if(isEncrypted){
FileEncryption.decryptStream(file, response.getOutputStream(),
start, end - start + 1, 20);
}
else {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())
) {
byte[] buffer = new byte[102400];
int readlength;
while ((readlength = bis.read(buffer)) > 0) {
bos.write(buffer, 0, readlength);
bos.flush();
}
}
}
} catch (Exception ignored) {
String url=request.getHeader("Referer");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("<script>top.$.jBox.tip('打开文件异常!','error');location.href='"+url+"'</script>");
throw ignored;
}
}
}