package com.vincent.rsf.server.common.utils;
|
|
import com.vincent.rsf.common.utils.Utils;
|
import com.vincent.rsf.framework.common.Cools;
|
import org.apache.tika.Tika;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.*;
|
import java.net.MalformedURLException;
|
import java.net.URLEncoder;
|
import java.text.SimpleDateFormat;
|
import java.util.*;
|
|
/**
|
* 文件上传下载工具类
|
*
|
* @author vincent
|
* @since 2018-12-14 08:38:53
|
*/
|
public class FileServerUtil {
|
// 除 text/* 外也需要设置输出编码的 content-type
|
private final static List<String> SET_CHARSET_CONTENT_TYPES = Arrays.asList(
|
"application/json",
|
"application/javascript"
|
);
|
|
/**
|
* 上传文件
|
*
|
* @param file MultipartFile
|
* @param directory 文件保存的目录
|
* @param uuidName 是否用uuid命名
|
* @return File
|
*/
|
public static File upload(MultipartFile file, String directory, boolean uuidName)
|
throws IOException, IllegalStateException {
|
File outFile = getUploadFile(file.getOriginalFilename(), directory, uuidName);
|
if (!outFile.getParentFile().exists()) {
|
if (!outFile.getParentFile().mkdirs()) {
|
throw new RuntimeException("make directory fail");
|
}
|
}
|
file.transferTo(outFile);
|
return outFile;
|
}
|
|
/**
|
* 上传base64格式文件
|
*
|
* @param base64 base64编码字符
|
* @param fileName 文件名称, 为空使用uuid命名
|
* @param directory 文件保存的目录
|
* @return File
|
*/
|
public static File upload(String base64, String fileName, String directory)
|
throws FileNotFoundException {
|
if (Cools.isEmpty(base64) || !base64.startsWith("data:image/") || !base64.contains(";base64,")) {
|
throw new RuntimeException("base64 data error");
|
}
|
String suffix = "." + base64.substring(11, base64.indexOf(";")); // 获取文件后缀
|
boolean uuidName = Cools.isEmpty(fileName);
|
File outFile = getUploadFile(uuidName ? suffix : fileName, directory, uuidName);
|
byte[] bytes = Base64.getDecoder().decode(base64.substring(base64.indexOf(";") + 8).getBytes());
|
|
FileOutputStream out = new FileOutputStream(outFile);
|
try {
|
out.write(bytes);
|
} catch (IOException e) {
|
throw new RuntimeException(e);
|
} finally {
|
try {
|
out.close();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
return outFile;
|
}
|
|
/**
|
* 获取上传文件位置
|
*
|
* @param name 文件名称
|
* @param directory 上传目录
|
* @param uuidName 是否使用uuid命名
|
* @return File
|
*/
|
public static File getUploadFile(String name, String directory, boolean uuidName) {
|
// 当前日期作为上传子目录
|
String dir = new SimpleDateFormat("yyyyMMdd/").format(new Date());
|
// 获取文件后缀
|
String suffix = (name == null || !name.contains(".")) ? "" : name.substring(name.lastIndexOf("."));
|
// 使用uuid命名
|
if (uuidName || name == null) {
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
|
return new File(directory, dir + uuid + suffix);
|
}
|
// 使用原名称, 存在相同则加(1)
|
File file = new File(directory, dir + name);
|
String prefix = Utils.removeSuffix(name, suffix);
|
int sameSize = 2;
|
while (file.exists()) {
|
file = new File(directory, dir + prefix + "(" + sameSize + ")" + suffix);
|
sameSize++;
|
}
|
return file;
|
}
|
|
/**
|
* 查看文件, 支持断点续传
|
*
|
* @param file 文件
|
* @param pdfDir office转pdf输出目录
|
* @param officeHome openOffice安装目录
|
* @param response HttpServletResponse
|
* @param request HttpServletRequest
|
*/
|
public static void preview(File file, String pdfDir, String officeHome,
|
HttpServletResponse response, HttpServletRequest request) {
|
preview(file, false, null, pdfDir, officeHome, response, request);
|
}
|
|
/**
|
* 查看文件, 支持断点续传
|
*
|
* @param file 文件
|
* @param forceDownload 是否强制下载
|
* @param fileName 强制下载的文件名称
|
* @param pdfDir office转pdf输出目录
|
* @param officeHome openOffice安装目录
|
* @param response HttpServletResponse
|
* @param request HttpServletRequest
|
*/
|
public static void preview(File file, boolean forceDownload, String fileName, String pdfDir, String officeHome,
|
HttpServletResponse response, HttpServletRequest request) {
|
CommonUtil.addCrossHeaders(response);
|
if (file == null || !file.exists()) {
|
outNotFund(response);
|
return;
|
}
|
if (forceDownload) {
|
setDownloadHeader(response, Cools.isEmpty(fileName) ? file.getName() : fileName);
|
} else {
|
// office转pdf预览
|
if (OpenOfficeUtil.canConverter(file.getName())) {
|
File pdfFile = OpenOfficeUtil.converterToPDF(file.getAbsolutePath(), pdfDir, officeHome);
|
if (pdfFile != null) {
|
file = pdfFile;
|
}
|
}
|
// 获取文件类型
|
String contentType = getContentType(file);
|
if (contentType != null) {
|
response.setContentType(contentType);
|
// 设置编码
|
if (contentType.startsWith("text/") || SET_CHARSET_CONTENT_TYPES.contains(contentType)) {
|
try {
|
String charset = JChardetFacadeUtil.detectCodepage(file.toURI().toURL());
|
if (charset != null) {
|
response.setCharacterEncoding(charset);
|
}
|
} catch (MalformedURLException e) {
|
e.printStackTrace();
|
}
|
}
|
} else {
|
setDownloadHeader(response, file.getName());
|
}
|
}
|
response.setHeader("Cache-Control", "public");
|
output(file, response, request);
|
}
|
|
/**
|
* 查看缩略图
|
*
|
* @param file 原文件
|
* @param thumbnail 缩略图文件
|
* @param size 缩略图文件的最大值(kb)
|
* @param response HttpServletResponse
|
* @param request HttpServletRequest
|
*/
|
public static void previewThumbnail(File file, File thumbnail, Integer size,
|
HttpServletResponse response, HttpServletRequest request) {
|
// 如果是图片并且缩略图不存在则生成
|
// if (!thumbnail.exists() && isImage(file)) {
|
// long fileSize = file.length();
|
// if ((fileSize / 1024) > size) {
|
// try {
|
// if (thumbnail.getParentFile().mkdirs()) {
|
// ImgUtil.scale(file, thumbnail, size / (fileSize / 1024f));
|
// if (thumbnail.exists() && thumbnail.length() > file.length()) {
|
// FileUtil.copy(file, thumbnail, true);
|
// }
|
// }
|
// } catch (Exception e) {
|
// e.printStackTrace();
|
// }
|
// } else {
|
// preview(file, null, null, response, request);
|
// return;
|
// }
|
// }
|
// preview(thumbnail.exists() ? thumbnail : file, null, null, response, request);
|
|
preview(file, null, null, response, request);
|
}
|
|
/**
|
* 输出文件流, 支持断点续传
|
*
|
* @param file 文件
|
* @param response HttpServletResponse
|
* @param request HttpServletRequest
|
*/
|
public static void output(File file, HttpServletResponse response, HttpServletRequest request) {
|
long length = file.length(); // 文件总大小
|
long start = 0, to = length - 1; // 开始读取位置, 结束读取位置
|
long lastModified = file.lastModified(); // 文件修改时间
|
response.setHeader("Accept-Ranges", "bytes");
|
response.setHeader("ETag", "\"" + length + "-" + lastModified + "\"");
|
response.setHeader("Last-Modified", new Date(lastModified).toString());
|
String range = request.getHeader("Range");
|
if (range != null) {
|
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
String[] ranges = range.replace("bytes=", "").split("-");
|
start = Long.parseLong(ranges[0].trim());
|
if (ranges.length > 1) {
|
to = Long.parseLong(ranges[1].trim());
|
}
|
response.setHeader("Content-Range", "bytes " + start + "-" + to + "/" + length);
|
}
|
response.setHeader("Content-Length", String.valueOf(to - start + 1));
|
try {
|
output(file, response.getOutputStream(), 2048, start, to);
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* 输出文件流
|
*
|
* @param file 文件
|
* @param os 输出流
|
*/
|
public static void output(File file, OutputStream os) {
|
output(file, os, null);
|
}
|
|
/**
|
* 输出文件流
|
*
|
* @param file 文件
|
* @param os 输出流
|
* @param size 读取缓冲区大小
|
*/
|
public static void output(File file, OutputStream os, Integer size) {
|
output(file, os, size, null, null);
|
}
|
|
/**
|
* 输出文件流, 支持分片
|
*
|
* @param file 文件
|
* @param os 输出流
|
* @param size 读取缓冲区大小
|
* @param start 开始位置
|
* @param to 结束位置
|
*/
|
public static void output(File file, OutputStream os, Integer size, Long start, Long to) {
|
BufferedInputStream is = null;
|
try {
|
is = new BufferedInputStream(new FileInputStream(file));
|
if (start != null) {
|
long skip = is.skip(start);
|
if (skip < start) {
|
System.out.println("ERROR: skip fail[ skipped=" + skip + ", start= " + start + " ]");
|
}
|
to = to - start + 1;
|
}
|
byte[] bytes = new byte[size == null ? 2048 : size];
|
int len;
|
if (to == null) {
|
while ((len = is.read(bytes)) != -1) {
|
os.write(bytes, 0, len);
|
}
|
} else {
|
while (to > 0 && (len = is.read(bytes)) != -1) {
|
os.write(bytes, 0, to < len ? (int) ((long) to) : len);
|
to -= len;
|
}
|
}
|
os.flush();
|
} catch (IOException ignored) {
|
} catch (Exception e) {
|
e.printStackTrace();
|
} finally {
|
if (os != null) {
|
try {
|
os.close();
|
} catch (IOException ignored) {
|
}
|
}
|
if (is != null) {
|
try {
|
is.close();
|
} catch (IOException e) {
|
System.out.println(e.getMessage());
|
}
|
}
|
}
|
}
|
|
/**
|
* 获取文件类型
|
*
|
* @param file 文件
|
* @return String
|
*/
|
public static String getContentType(File file) {
|
String contentType = null;
|
if (file.exists()) {
|
try {
|
contentType = new Tika().detect(file);
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
return contentType;
|
}
|
|
/**
|
* 判断文件是否是图片类型
|
*
|
* @param file 文件
|
* @return boolean
|
*/
|
public static boolean isImage(File file) {
|
return isImage(getContentType(file));
|
}
|
|
/**
|
* 判断文件是否是图片类型
|
*
|
* @param contentType 文件类型
|
* @return boolean
|
*/
|
public static boolean isImage(String contentType) {
|
return contentType != null && contentType.startsWith("image/");
|
}
|
|
/**
|
* 设置下载文件的header
|
*
|
* @param response HttpServletResponse
|
* @param fileName 文件名称
|
*/
|
public static void setDownloadHeader(HttpServletResponse response, String fileName) {
|
response.setContentType("application/force-download");
|
try {
|
fileName = URLEncoder.encode(fileName, "utf-8");
|
} catch (UnsupportedEncodingException e) {
|
e.printStackTrace();
|
}
|
response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
|
}
|
|
/**
|
* 输出404错误页面
|
*
|
* @param response HttpServletResponse
|
*/
|
public static void outNotFund(HttpServletResponse response) {
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
outMessage("404 Not Found", null, response);
|
}
|
|
/**
|
* 输出错误页面
|
*
|
* @param title 标题
|
* @param message 内容
|
* @param response HttpServletResponse
|
*/
|
public static void outMessage(String title, String message, HttpServletResponse response) {
|
response.setContentType("text/html;charset=UTF-8");
|
try {
|
PrintWriter writer = response.getWriter();
|
writer.write("<!doctype html>");
|
writer.write("<title>" + title + "</title>");
|
writer.write("<h1 style=\"text-align: center\">" + title + "</h1>");
|
if (message != null) {
|
writer.write(message);
|
}
|
writer.write("<hr/><p style=\"text-align: center\">vincent File Server</p>");
|
writer.flush();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
|
}
|