|
@@ -0,0 +1,397 @@
|
|
|
+package cn.com.qmth.examcloud.support.filestorage;
|
|
|
+
|
|
|
+import java.io.DataInputStream;
|
|
|
+import java.io.DataOutputStream;
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.net.HttpURLConnection;
|
|
|
+import java.net.URL;
|
|
|
+
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+
|
|
|
+import cn.com.qmth.examcloud.commons.exception.StatusException;
|
|
|
+import cn.com.qmth.examcloud.commons.util.MD5;
|
|
|
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
|
|
|
+import cn.com.qmth.examcloud.support.cache.bean.SysPropertyCacheBean;
|
|
|
+import cn.com.qmth.examcloud.web.aliyun.AliyunSiteManager;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.FileStorage;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.FileStorageHelper;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.FileStorageType;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.YunHttpRequest;
|
|
|
+import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;
|
|
|
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
|
|
|
+import cn.com.qmth.examcloud.web.upyun.UpyunSiteManager;
|
|
|
+
|
|
|
+/**文件存储服务接口工具类
|
|
|
+ * @author 86182
|
|
|
+ *
|
|
|
+ */
|
|
|
+public class FileStorageUtil {
|
|
|
+
|
|
|
+// private static String fileStorageType = PropertyHolder.getString("$filestorage-type");
|
|
|
+
|
|
|
+// private static String tempDir = PropertyHolder.getString("$filestorage-trans-tempdir");
|
|
|
+
|
|
|
+ private static String beanSuff = "FileStorage";
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据当前配置存储类型保存文件到存储服务器
|
|
|
+ *
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param in 文件流
|
|
|
+ * @param md5 文件MD5 可为空
|
|
|
+ * @return 返回包含协议名的地址,数据库直接存储用 如:upyun-1://student_photo/001.jpg
|
|
|
+ */
|
|
|
+ public static YunPathInfo saveFile(String siteId, FileStoragePathEnvInfo env, InputStream in,String md5) {
|
|
|
+ FileStorageType fsType = getFileStorageType();
|
|
|
+ return saveFile(siteId, env, in, fsType,md5);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据当前配置存储类型保存文件到存储服务器
|
|
|
+ *
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param file 文件
|
|
|
+ * @param withMd5 是否校验MD5
|
|
|
+ * @return 返回包含协议名的地址,数据库直接存储用 如:upyun-1://student_photo/001.jpg
|
|
|
+ */
|
|
|
+ public static YunPathInfo saveFile(String siteId, FileStoragePathEnvInfo env, File file,boolean withMd5) {
|
|
|
+ FileStorageType fsType = getFileStorageType();
|
|
|
+ String md5 = null;
|
|
|
+ if (withMd5) {
|
|
|
+ md5 = MD5.md5Hex(file);
|
|
|
+ }
|
|
|
+ return saveFile(siteId, env, file, fsType,md5);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 根据当前配置存储类型保存文件到存储服务器
|
|
|
+ *
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param file 文件
|
|
|
+ * @param md5 文件MD5 可为空
|
|
|
+ * @return 返回包含协议名的地址,数据库直接存储用 如:upyun-1://student_photo/001.jpg
|
|
|
+ */
|
|
|
+ public static YunPathInfo saveFile(String siteId, FileStoragePathEnvInfo env, File file,String md5) {
|
|
|
+ FileStorageType fsType = getFileStorageType();
|
|
|
+ return saveFile(siteId, env, file, fsType,md5);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 根据存储类型保存文件到存储服务器
|
|
|
+ *
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param in 文件流
|
|
|
+ * @param fsType 存储类型
|
|
|
+ * @param md5 文件MD5 可为空
|
|
|
+ * @return 返回包含协议名的地址,数据库直接存储用 如:upyun-1://student_photo/001.jpg
|
|
|
+ */
|
|
|
+ private static YunPathInfo saveFile(String siteId, FileStoragePathEnvInfo env, InputStream in, FileStorageType fsType,String md5) {
|
|
|
+
|
|
|
+ if (siteId == null) {
|
|
|
+ throw new StatusException("1000", "siteId是空");
|
|
|
+ }
|
|
|
+ if (in == null) {
|
|
|
+ throw new StatusException("1001", "文件流是空");
|
|
|
+ }
|
|
|
+ if (env == null) {
|
|
|
+ throw new StatusException("1002", "文件上传路径信息是空");
|
|
|
+ }
|
|
|
+ env.setTimeMillis(String.valueOf(System.currentTimeMillis()));
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(fsType.name().toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ return fs.saveFile(siteId, env, in,md5);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 根据存储类型保存文件到存储服务器
|
|
|
+ *
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param file 文件
|
|
|
+ * @param fsType 存储类型
|
|
|
+ * @param md5 文件MD5 可为空
|
|
|
+ * @return 返回包含协议名的地址,数据库直接存储用 如:upyun-1://student_photo/001.jpg
|
|
|
+ */
|
|
|
+ private static YunPathInfo saveFile(String siteId, FileStoragePathEnvInfo env, File file, FileStorageType fsType,String md5) {
|
|
|
+ if (siteId == null) {
|
|
|
+ throw new StatusException("2000", "siteId是空");
|
|
|
+ }
|
|
|
+ if (file == null) {
|
|
|
+ throw new StatusException("2001", "文件是空");
|
|
|
+ }
|
|
|
+ if (env == null) {
|
|
|
+ throw new StatusException("2002", "文件上传路径信息是空");
|
|
|
+ }
|
|
|
+ env.setTimeMillis(String.valueOf(System.currentTimeMillis()));
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(fsType.name().toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ return fs.saveFile(siteId, env, file,md5);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件访问路径
|
|
|
+ *
|
|
|
+ * @param path 数据库保存的路径 如:upyun-1://student_photo/001.jpg,兼容老数据,路径必须是包含根路径的
|
|
|
+ * @return 可直接访问的文件地址
|
|
|
+ */
|
|
|
+ public static String realPath(String path) {
|
|
|
+ if(StringUtils.isBlank(path)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(path)) {
|
|
|
+ throw new StatusException("3001", "文件路径是空");
|
|
|
+ }
|
|
|
+ // 兼容处理老数据文件路径
|
|
|
+ path = diposeOldPath(path);
|
|
|
+ // 如果是全路径直接返回
|
|
|
+ if (path.startsWith("http") || path.startsWith("https")) {
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+ // 根据路径头获取对应的处理类
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(FileStorageHelper.getHead(path).toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ // 返回处理类处理结果
|
|
|
+ return fs.realPath(path);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将文件转换到指定存储服务上
|
|
|
+ *
|
|
|
+ * @param path 数据库保存的路径
|
|
|
+ * 如:upyun-1://student_photo/001.jpg,兼容老数据,路径必须是包含根路径的
|
|
|
+ * @param transFsType 要转换的类型
|
|
|
+ * @return 转换之后的路径
|
|
|
+ */
|
|
|
+// public static String transPath(String path, FileStorageType transFsType) {
|
|
|
+//
|
|
|
+// File file = null;
|
|
|
+// if (StringUtils.isBlank(path)) {
|
|
|
+// throw new StatusException("4001", "文件路径是空");
|
|
|
+// }
|
|
|
+// if (transFsType == null) {
|
|
|
+// throw new StatusException("4002", "转换类型是空");
|
|
|
+// }
|
|
|
+// // 兼容处理老数据文件路径
|
|
|
+// path = diposeOldPath(path);
|
|
|
+// // 如果是全路径直接返回
|
|
|
+// if (path.startsWith("http") || path.startsWith("https")) {
|
|
|
+// return path;
|
|
|
+// }
|
|
|
+//
|
|
|
+// FileStorageType sourseType = FileStorageType.valueOf(FileStorageHelper.getHead(path));
|
|
|
+// // 相同的类型直接返回
|
|
|
+// if (sourseType.equals(transFsType)) {
|
|
|
+// return path;
|
|
|
+// }
|
|
|
+// // 获取对应的处理类
|
|
|
+// FileStorage fs = SpringContextHolder.getBean(transFsType.name().toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+// try {
|
|
|
+// // 下载文件到本地
|
|
|
+// String httppath = fs.realPath(FileStorageHelper.getPath(path));
|
|
|
+// file = downFile(httppath);
|
|
|
+// // 保存文件到指定存储服务器,并返回路径
|
|
|
+// YunPathInfo ret = fs.saveFile(file, "from" + sourseType.name().toLowerCase() + "/" + FileStorageHelper.getPath(path));
|
|
|
+// return ret.getRelativePath();
|
|
|
+// } catch (IOException e) {
|
|
|
+// throw new StatusException("4003", "下载文件出错 " + e.getMessage());
|
|
|
+// } finally {
|
|
|
+// if (file != null) {
|
|
|
+// file.delete();
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// }
|
|
|
+
|
|
|
+// private static File downFile(String url) throws IOException {
|
|
|
+// String name = url.substring(url.lastIndexOf("/") + 1);
|
|
|
+// File file = new File(tempDir + "/" + name);
|
|
|
+// if (file.exists()) {
|
|
|
+// file.delete();
|
|
|
+// }
|
|
|
+// file.createNewFile();
|
|
|
+// saveUrlAs(url, file.getAbsolutePath());
|
|
|
+// return file;
|
|
|
+// }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param path 数据库存储的路径。路径必须是包含根路径的
|
|
|
+ * @return 处理后的路径。补全路径头。
|
|
|
+ */
|
|
|
+ private static String diposeOldPath(String path) {
|
|
|
+ if(StringUtils.isBlank(path)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 如果是全路径直接返回
|
|
|
+ if (path.startsWith("http") || path.startsWith("https")) {
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+ // 如果是半路径,转换成又拍云类型
|
|
|
+ if (path.indexOf(":") == -1) {
|
|
|
+ if (path.startsWith("/")) {
|
|
|
+ path = path.substring(1);
|
|
|
+ }
|
|
|
+ return FileStorageHelper.getUpyunSiteOne(path);
|
|
|
+ }
|
|
|
+ // 无需处理
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将网络文件保存到本地
|
|
|
+ *
|
|
|
+ * @param fileUrl 网络文件URL
|
|
|
+ * @param localFilePath 例如D:/123.txt
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+ private static void saveUrlAs(String fileUrl, String localFilePath) throws IOException {
|
|
|
+ URL url = new URL(fileUrl);
|
|
|
+
|
|
|
+ HttpURLConnection connection;
|
|
|
+ connection = (HttpURLConnection) url.openConnection();
|
|
|
+
|
|
|
+ try (DataInputStream dataInputStream = new DataInputStream(connection.getInputStream());
|
|
|
+ FileOutputStream fileOutputStream = new FileOutputStream(localFilePath);
|
|
|
+ DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);) {
|
|
|
+
|
|
|
+ byte[] buffer = new byte[4096];
|
|
|
+ int count;
|
|
|
+ while ((count = dataInputStream.read(buffer)) > 0) {
|
|
|
+ dataOutputStream.write(buffer, 0, count);
|
|
|
+ }
|
|
|
+ fileOutputStream.flush();
|
|
|
+ dataOutputStream.flush();
|
|
|
+ } finally {
|
|
|
+ if (connection != null) {
|
|
|
+ connection.disconnect();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**将网络文件保存到本地
|
|
|
+ * @param fileUrl 网络文件URL
|
|
|
+ * @param localFile 本地文件对象
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+ public static void saveUrlAs(String fileUrl, File localFile) {
|
|
|
+ try {
|
|
|
+ saveUrlAs(fileUrl, localFile.getAbsolutePath());
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new StatusException("5001", "下载文件出错",e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**获取指定云存储的签名
|
|
|
+ * @param siteId
|
|
|
+ * @param env
|
|
|
+ * @param md5
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static YunHttpRequest getSignature(FileStorageType fsType,String siteId, FileStoragePathEnvInfo env, String md5) {
|
|
|
+ env.setTimeMillis(String.valueOf(System.currentTimeMillis()));
|
|
|
+ // 获取对应的处理类
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(fsType.name().toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ return fs.getSignature(siteId, env, md5);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**获取完整的不带域名的路径
|
|
|
+ * @param prefixPath 路径前缀
|
|
|
+ * @param sourcePath 数据库存储的路径
|
|
|
+ * @return 处理之后的路径
|
|
|
+ */
|
|
|
+ public static String getIntactPath(String prefixPath,String sourcePath) {
|
|
|
+ if(StringUtils.isBlank(sourcePath)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 如果是全路径直接返回
|
|
|
+ if (sourcePath.startsWith("http") || sourcePath.startsWith("https")) {
|
|
|
+ return sourcePath;
|
|
|
+ }
|
|
|
+ // 如果是半路径则拼接
|
|
|
+ if (sourcePath.indexOf(":") == -1) {
|
|
|
+ if (sourcePath.startsWith("/")) {
|
|
|
+ sourcePath = sourcePath.substring(1);
|
|
|
+ }
|
|
|
+ if (prefixPath.endsWith("/")) {
|
|
|
+ prefixPath = prefixPath.substring(0,prefixPath.length());
|
|
|
+ }
|
|
|
+ return prefixPath+"/"+sourcePath;
|
|
|
+ }
|
|
|
+ // 新协议无需处理
|
|
|
+ return sourcePath;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**删除云存储文件
|
|
|
+ * @param path 全路径,包含根目录,含协议名
|
|
|
+ */
|
|
|
+ public static void deleteFile(String path) {
|
|
|
+ if(StringUtils.isBlank(path)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ path=diposeOldPath(path);
|
|
|
+ // 如果是全路径直接返回
|
|
|
+ if (path.startsWith("http") || path.startsWith("https")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(FileStorageHelper.getHead(path).toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ fs.deleteFile(path);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件访问路径(备用域名地址)
|
|
|
+ *
|
|
|
+ * @param path 数据库保存的路径 如:upyun-1://student_photo/001.jpg,兼容老数据,路径必须是包含根路径的
|
|
|
+ * @return 可直接访问的文件地址,如果没有配置备用地址,则返回主域名地址
|
|
|
+ */
|
|
|
+ public static String realPathBackup(String path) {
|
|
|
+ if(StringUtils.isBlank(path)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(path)) {
|
|
|
+ throw new StatusException("6001", "文件路径是空");
|
|
|
+ }
|
|
|
+ // 兼容处理老数据文件路径
|
|
|
+ path = diposeOldPath(path);
|
|
|
+ // 如果是全路径直接返回
|
|
|
+ if (path.startsWith("http") || path.startsWith("https")) {
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+ // 根据路径头获取对应的处理类
|
|
|
+ FileStorage fs = SpringContextHolder.getBean(FileStorageHelper.getHead(path).toLowerCase() + beanSuff, FileStorage.class);
|
|
|
+ // 返回处理类处理结果
|
|
|
+ return fs.realPathBackup(path);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static void initYunClient() {
|
|
|
+ UpyunSiteManager.initClient();
|
|
|
+ AliyunSiteManager.initClient();
|
|
|
+ }
|
|
|
+ public static void initYunSite() {
|
|
|
+ UpyunSiteManager.initSite();
|
|
|
+ AliyunSiteManager.initSite();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static FileStorageType getFileStorageType() {
|
|
|
+ SysPropertyCacheBean spc = CacheHelper.getSysProperty("filestorage.type");
|
|
|
+ if (spc == null || spc.getValue() == null) {
|
|
|
+ throw new StatusException("10001", "未配置文件存储服务类型");
|
|
|
+ }
|
|
|
+ String type=(String)spc.getValue();
|
|
|
+ try {
|
|
|
+ return FileStorageType.valueOf(type);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new StatusException("10002", "配置文件存储服务类型错误");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|