Ver código fonte

文件存储通用方法

xiatian 5 anos atrás
pai
commit
b536db6ba3

+ 10 - 1
pom.xml

@@ -1,5 +1,8 @@
 <?xml version="1.0"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>cn.com.qmth.examcloud</groupId>
@@ -116,6 +119,12 @@
 			<artifactId>metrics-core</artifactId>
 		</dependency>
 
+		<dependency>
+			<groupId>com.aliyun.oss</groupId>
+			<artifactId>aliyun-sdk-oss</artifactId>
+			<version>3.8.0</version>
+		</dependency>
+
 	</dependencies>
 
 </project>

+ 63 - 0
src/main/java/cn/com/qmth/examcloud/web/aliyun/AliYunAccount.java

@@ -0,0 +1,63 @@
+package cn.com.qmth.examcloud.web.aliyun;
+
+/**
+ *阿里云存储账号信息
+ */
+public class AliYunAccount {
+
+	private String bucket;
+	private String ossEndpoint;
+	private String accessKeyId;
+	private String accessKeySecret;
+	private String domain;
+
+	public String getBucket() {
+		return bucket;
+	}
+
+	public void setBucket(String bucket) {
+		this.bucket = bucket;
+	}
+
+	public String getOssEndpoint() {
+		return ossEndpoint;
+	}
+
+	public void setOssEndpoint(String ossEndpoint) {
+		this.ossEndpoint = ossEndpoint;
+	}
+
+	public String getAccessKeyId() {
+		return accessKeyId;
+	}
+
+	public void setAccessKeyId(String accessKeyId) {
+		this.accessKeyId = accessKeyId;
+	}
+
+	public String getAccessKeySecret() {
+		return accessKeySecret;
+	}
+
+	public void setAccessKeySecret(String accessKeySecret) {
+		this.accessKeySecret = accessKeySecret;
+	}
+
+	public String getDomain() {
+		return domain;
+	}
+
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+
+	public AliYunAccount(String bucket, String ossEndpoint, String accessKeyId, String accessKeySecret, String domain) {
+		super();
+		this.bucket = bucket;
+		this.ossEndpoint = ossEndpoint;
+		this.accessKeyId = accessKeyId;
+		this.accessKeySecret = accessKeySecret;
+		this.domain = domain;
+	}
+
+}

+ 45 - 0
src/main/java/cn/com/qmth/examcloud/web/aliyun/AliYunHttpRequest.java

@@ -0,0 +1,45 @@
+package cn.com.qmth.examcloud.web.aliyun;
+
+import java.util.Map;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class AliYunHttpRequest implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3359164974369297063L;
+
+	private String accessUrl;
+
+	private String formUrl;
+
+	private Map<String, String> formParams;
+
+	public String getAccessUrl() {
+		return accessUrl;
+	}
+
+	public void setAccessUrl(String accessUrl) {
+		this.accessUrl = accessUrl;
+	}
+
+	public String getFormUrl() {
+		return formUrl;
+	}
+
+	public void setFormUrl(String formUrl) {
+		this.formUrl = formUrl;
+	}
+
+	public Map<String, String> getFormParams() {
+		return formParams;
+	}
+
+	public void setFormParams(Map<String, String> formParams) {
+		this.formParams = formParams;
+	}
+
+}

+ 64 - 0
src/main/java/cn/com/qmth/examcloud/web/aliyun/AliyunSite.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.examcloud.web.aliyun;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ */
+public class AliyunSite implements JsonSerializable {
+
+	private static final long serialVersionUID = -1754474062438702321L;
+
+	private String id;
+
+	private String name;
+
+	private String aliyunId;
+
+	private String maxSize;
+
+	private String path;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getMaxSize() {
+		return maxSize;
+	}
+
+	public void setMaxSize(String maxSize) {
+		this.maxSize = maxSize;
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	public String getAliyunId() {
+		return aliyunId;
+	}
+
+	public void setAliyunId(String aliyunId) {
+		this.aliyunId = aliyunId;
+	}
+
+	
+}

+ 105 - 0
src/main/java/cn/com/qmth/examcloud/web/aliyun/AliyunSiteManager.java

@@ -0,0 +1,105 @@
+package cn.com.qmth.examcloud.web.aliyun;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.thoughtworks.xstream.XStream;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.XStreamBuilder;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+
+/**
+ * aliyun site manager
+ *
+ */
+public class AliyunSiteManager {
+
+	private static final Map<String, AliyunSite> SITE_HOLDERS = new ConcurrentHashMap<>();
+
+	private static final Map<String, AliYunAccount> ACCOUNT_HOLDERS = new ConcurrentHashMap<>();
+
+	public static void init() {
+		String resoucePath = PathUtil.getResoucePath("aliyun.xml");
+		File file = new File(resoucePath);
+
+		XStream xStream = XStreamBuilder.newInstance().build();
+		xStream.allowTypes(new Class[] { AliyunSite.class, List.class });
+		xStream.alias("sites", List.class);
+		xStream.alias("site", AliyunSite.class);
+
+		List<AliyunSite> list = null;
+		try {
+			@SuppressWarnings("unchecked")
+			List<AliyunSite> obj = (List<AliyunSite>) xStream.fromXML(file);
+			list = obj;
+		} catch (Exception e) {
+			throw new StatusException("520001", "aliyun.xml is wrong", e);
+		}
+
+		for (AliyunSite aliyunSite : list) {
+
+			SITE_HOLDERS.put(aliyunSite.getId(), aliyunSite);
+
+			String aliyunId = aliyunSite.getAliyunId();
+			String bucket = PropertyHolder.getString("$aliyun.site." + aliyunId + ".bucket");
+			String ossEndpoint = PropertyHolder.getString("$aliyun.site." + aliyunId + ".ossEndpoint");
+
+			String accessKeyId = PropertyHolder.getString("$aliyun.site." + aliyunId + ".accessKeyId");
+			String accessKeySecret = PropertyHolder.getString("$aliyun.site." + aliyunId + ".accessKeySecret");
+			String domain = PropertyHolder.getString("$aliyun.site." + aliyunId + ".domain");
+
+			if (StringUtils.isBlank(bucket)) {
+				throw new StatusException("520001", "bucket is not configured. aliyunId=" + aliyunId);
+			}
+			if (StringUtils.isBlank(ossEndpoint)) {
+				throw new StatusException("520002", "ossEndpoint is not configured. aliyunId=" + aliyunId);
+			}
+			if (StringUtils.isBlank(accessKeyId)) {
+				throw new StatusException("520003", "accessKeyId is not configured. aliyunId=" + aliyunId);
+			}
+			if (StringUtils.isBlank(accessKeySecret)) {
+				throw new StatusException("520004", "accessKeySecret is not configured. aliyunId=" + aliyunId);
+			}
+			if (StringUtils.isBlank(domain)) {
+				throw new StatusException("520005", "domain is not configured. aliyunId=" + aliyunId);
+			}
+			if (null == ACCOUNT_HOLDERS.get(aliyunId)) {
+				AliYunAccount ac = new AliYunAccount(bucket, ossEndpoint, accessKeyId, accessKeySecret, domain);
+				ACCOUNT_HOLDERS.put(aliyunId, ac);
+			}
+		}
+
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author
+	 * @param siteId
+	 * @return
+	 */
+	public static AliyunSite getAliyunSite(String siteId) {
+		AliyunSite aliyunSite = SITE_HOLDERS.get(siteId);
+
+		if (null == aliyunSite) {
+			throw new StatusException("520006", "aliyunSite is null");
+		}
+		return aliyunSite;
+	}
+	
+	public static AliYunAccount getAliYunAccountByAliyunId(String aliyunId) {
+		AliYunAccount ac = ACCOUNT_HOLDERS.get(aliyunId);
+
+		if (null == ac) {
+			throw new StatusException("520007", "AliYunAccount is null");
+		}
+		return ac;
+	}
+
+}

+ 27 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/FileStorage.java

@@ -0,0 +1,27 @@
+package cn.com.qmth.examcloud.web.filestorage;
+
+import java.io.File;
+
+public interface FileStorage {
+	/**保存文件到存储服务
+	 * @param siteId 
+	 * @param env
+	 * @param file 文件
+	 * @return 返回包含协议名的地址,数据库直接存储用
+	 */
+	public String saveFile(String siteId,FileStoragePathEnvInfo env,File file);
+	
+	/**获取可直接访问的文件地址
+	 * @param path 全路径,包含根目录,不含协议名
+	 * @return 返回可直接访问的地址
+	 */
+	public String realPath(String path);
+	
+	/**保存文件到存储服务,siteId为1,转换接口用
+	 * @param file 文件
+	 * @param path 全路径,包含根目录,不含协议名
+	 * @return 返回包含协议名的地址,数据库直接存储用
+	 */
+	public String saveFile(File file, String path);
+	
+}

+ 160 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/FileStoragePathEnvInfo.java

@@ -0,0 +1,160 @@
+package cn.com.qmth.examcloud.web.filestorage;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 云存储路径变量
+ *
+ */
+public class FileStoragePathEnvInfo implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5101258662105160433L;
+
+	/**
+	 * 顶级机构ID
+	 */
+	private String rootOrgId;
+
+	/**
+	 * 顶级机构域名
+	 */
+	private String rootOrgDomain;
+
+	/**
+	 * 用户ID(包含普普通用户ID和studentId)
+	 */
+	private String userId;
+
+	/**
+	 * 时间戳
+	 */
+	private String timeMillis;
+
+	/**
+	 * 文件后缀(以"."开头,如 ".jpg",".zip")
+	 */
+	private String fileSuffix;
+
+	/**
+	 * 相对路径
+	 */
+	private String relativePath;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext1;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext2;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext3;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext4;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext5;
+
+	public String getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(String rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getRootOrgDomain() {
+		return rootOrgDomain;
+	}
+
+	public void setRootOrgDomain(String rootOrgDomain) {
+		this.rootOrgDomain = rootOrgDomain;
+	}
+
+	public String getUserId() {
+		return userId;
+	}
+
+	public void setUserId(String userId) {
+		this.userId = userId;
+	}
+
+	public String getTimeMillis() {
+		return timeMillis;
+	}
+
+	public void setTimeMillis(String timeMillis) {
+		this.timeMillis = timeMillis;
+	}
+
+	public String getFileSuffix() {
+		return fileSuffix;
+	}
+
+	public void setFileSuffix(String fileSuffix) {
+		this.fileSuffix = fileSuffix;
+	}
+
+	public String getRelativePath() {
+		return relativePath;
+	}
+
+	public void setRelativePath(String relativePath) {
+		this.relativePath = relativePath;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+}

+ 66 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/FileStorageSite.java

@@ -0,0 +1,66 @@
+package cn.com.qmth.examcloud.web.filestorage;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 
+ */
+public class FileStorageSite implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4367301242875120606L;
+
+	private String id;
+
+	private String name;
+
+	private String upyunId;
+
+	private String maxSize;
+
+	private String path;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getUpyunId() {
+		return upyunId;
+	}
+
+	public void setUpyunId(String upyunId) {
+		this.upyunId = upyunId;
+	}
+
+	public String getMaxSize() {
+		return maxSize;
+	}
+
+	public void setMaxSize(String maxSize) {
+		this.maxSize = maxSize;
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+}

+ 17 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/FileStorageType.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.web.filestorage;
+
+public enum FileStorageType {
+
+	/**
+	 * 又拍云存储
+	 */
+	UPYUN,
+
+	/**
+	 * 阿里云存储
+	 */
+	ALIYUN,
+
+	
+
+}

+ 218 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/FileStorageUtil.java

@@ -0,0 +1,218 @@
+package cn.com.qmth.examcloud.web.filestorage;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+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.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
+public class FileStorageUtil {
+
+	private static String fileStorageType = PropertyHolder.getString("$FileStorageType");
+
+	private static String tempDir = PropertyHolder.getString("$FileStorageTransTempDir");
+
+	private static String beanSuff = "FileStorage";
+
+	private static String connector = "://";
+
+	/**根据当前配置存储类型保存文件到存储服务器
+	 * @param siteId
+	 * @param env
+	 * @param file 文件
+	 * @return 返回包含协议名的地址,数据库直接存储用  如:upyun-1://student_photo/001.jpg
+	 */
+	public static String saveFile(String siteId,FileStoragePathEnvInfo env,File file) {
+		FileStorageType fsType = FileStorageType.valueOf(fileStorageType);
+		return saveFile(siteId, env, file, fsType);
+	}
+
+	/**根据存储类型保存文件到存储服务器
+	 * @param siteId
+	 * @param env
+	 * @param file 文件
+	 * @param fsType 存储类型
+	 * @return 返回包含协议名的地址,数据库直接存储用  如:upyun-1://student_photo/001.jpg
+	 */
+	private static String saveFile(String siteId,FileStoragePathEnvInfo env,File file, FileStorageType fsType) {
+		if (siteId == null) {
+			throw new StatusException("2000", "siteId是空");
+		}
+		if (file == null) {
+			throw new StatusException("2001", "文件是空");
+		}
+		if (env==null) {
+			throw new StatusException("2002", "文件上传路径信息是空");
+		}
+		FileStorage fs = SpringContextHolder.getBean(fsType.name().toLowerCase() + beanSuff, FileStorage.class);
+		return fs.saveFile(siteId, env, file);
+	}
+
+	/**
+	 * 获取文件访问路径
+	 * 
+	 * @param path 数据库保存的路径 如:upyun-1://student_photo/001.jpg,兼容老数据,路径必须是包含根路径的
+	 * @return 可直接访问的文件地址
+	 */
+	public static String realPath(String path) {
+
+		if (StringUtils.isBlank(path)) {
+			throw new StatusException("3001", "文件路径是空");
+		}
+		// 兼容处理老数据文件路径
+		path = diposeOldPath(path);
+		// 如果是全路径直接返回
+		if (path.startsWith("http") || path.startsWith("https")) {
+			return path;
+		}
+		// 根据路径头获取对应的处理类
+		FileStorage fs = SpringContextHolder.getBean(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(getHead(path));
+		// 相同的类型直接返回
+		if (sourseType.equals(transFsType)) {
+			return path;
+		}
+		// 获取对应的处理类
+		FileStorage fs = SpringContextHolder.getBean(transFsType.name().toLowerCase() + beanSuff, FileStorage.class);
+		try {
+			// 下载文件到本地
+			String httppath = fs.realPath(getPath(path));
+			file = downFile(httppath);
+			// 保存文件到指定存储服务器,并返回路径
+			String ret = fs.saveFile(file, "from"+sourseType.name().toLowerCase()+"/"+getPath(path));
+			return ret;
+		} 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 (path.startsWith("http") || path.startsWith("https")) {
+			return path;
+		}
+		// 如果是半路径,转换成又拍云类型
+		if (path.indexOf(":") == -1) {
+			if (path.startsWith("/")) {
+				path = path.substring(1);
+			}
+			return FileStorageType.UPYUN.name().toLowerCase() + connector + path;
+		}
+		// 无需处理
+		return path;
+	}
+	
+	public static String getYunId(String path) {
+		String hpath = path.substring(0,path.indexOf(connector));
+		String yunId = hpath.substring(path.indexOf("-")+1);
+		return yunId;
+	}
+	
+	public static String getHead(String path) {
+		String hpath = path.substring(0,path.indexOf(connector));
+		String head = hpath.substring(0, hpath.indexOf("-"));
+		return head;
+	}
+
+	public static String getPath(String path) {
+		String rpath = path.substring(path.indexOf(connector) + 4);
+		return rpath;
+	}
+	
+	public static String getUrl(String domain,String path) {
+		if (path.startsWith("/")) {
+			path = path.substring(1);
+		}
+		if (domain.endsWith("/")) {
+			domain = domain.substring(0,domain.length());
+		}
+		return domain+"/"+path;
+	}
+
+	/**
+	 * 将网络文件保存到本地
+	 *
+	 * @param fileUrl       网络文件URL
+	 * @param localFilePath 例如D:/123.txt
+	 * @return
+	 * @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();
+			}
+		}
+	}
+}

+ 229 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/impl/AliyunFileStorageImpl.java

@@ -0,0 +1,229 @@
+package cn.com.qmth.examcloud.web.filestorage.impl;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.activation.MimetypesFileTypeMap;
+
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.FreeMarkerUtil;
+import cn.com.qmth.examcloud.web.aliyun.AliYunAccount;
+import cn.com.qmth.examcloud.web.aliyun.AliyunSite;
+import cn.com.qmth.examcloud.web.aliyun.AliyunSiteManager;
+import cn.com.qmth.examcloud.web.filestorage.FileStorage;
+import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
+import cn.com.qmth.examcloud.web.filestorage.FileStorageType;
+import cn.com.qmth.examcloud.web.filestorage.FileStorageUtil;
+
+@Service(value = "aliyunFileStorage")
+public class AliyunFileStorageImpl implements FileStorage {
+	private ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+	
+	// 文件最大大小(byte)
+	private static int maxFileSize = 100 * 1024 * 1024;
+
+
+	@Override
+	public String saveFile(File file, String path) {
+		try {
+			String siteId="transPath";
+			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
+			env.setRelativePath(path);
+			String relativePath=postObject(siteId, env, file, null);
+			AliyunSite as=AliyunSiteManager.getAliyunSite(siteId);
+			return FileStorageType.ALIYUN+"-"+as.getAliyunId()+"://"+relativePath;
+		} catch (IOException e) {
+			throw new StatusException("1001", "上传出错",e);
+		}
+	}
+
+	@Override
+	public String realPath(String path) {
+		String yunId=FileStorageUtil.getYunId(path);
+		AliYunAccount ac=AliyunSiteManager.getAliYunAccountByAliyunId(yunId);
+		return FileStorageUtil.getUrl(ac.getDomain(), path);
+	}
+
+	/**表单上传
+	 * @param siteId
+	 * @param env
+	 * @param file
+	 * @param md5
+	 * @return 不带域名的完整路径
+	 * @throws IOException
+	 */
+	private  String  postObject(String siteId, FileStoragePathEnvInfo env,File file,String md5) throws IOException {
+		AliyunSite as=AliyunSiteManager.getAliyunSite(siteId);
+		AliYunAccount ac=AliyunSiteManager.getAliYunAccountByAliyunId(as.getAliyunId());
+		String ossEndpoint=ac.getOssEndpoint();
+		String bucket=ac.getBucket();
+		String accessKeyId=ac.getAccessKeyId();
+		String accessKeySecret=ac.getAccessKeySecret();
+		//阿里云文件路径
+		String path = FreeMarkerUtil.process(as.getPath(), env);
+		if (path.startsWith("/")) {
+			path = path.substring(1);
+		}
+		
+		String filepath = file.getAbsolutePath();
+		String filename = path.substring(path.lastIndexOf("/") + 1);
+		String urlStr = ossEndpoint.replace("http://", "http://" + bucket + "."); // 提交表单的URL为bucket域名
+
+		LinkedHashMap<String, String> textMap = new LinkedHashMap<String, String>();
+		// key
+		textMap.put("key", path);
+		// Content-Disposition
+		textMap.put("Content-Disposition", "attachment;filename=" + filename);
+		// OSSAccessKeyId
+		textMap.put("OSSAccessKeyId", accessKeyId);
+		// policy
+		String policy = "{\"expiration\": \"2120-01-01T12:00:00.000Z\",\"conditions\": [[\"content-length-range\", 0, "
+				+ maxFileSize + "]]}";
+		String encodePolicy = java.util.Base64.getEncoder().encodeToString(policy.getBytes());
+		textMap.put("policy", encodePolicy);
+		// Signature
+		String signaturecom = com.aliyun.oss.common.auth.ServiceSignature.create().computeSignature(accessKeySecret,
+				encodePolicy);
+		textMap.put("Signature", signaturecom);
+
+		Map<String, String> fileMap = new HashMap<String, String>();
+		fileMap.put("file", filepath);
+
+		String ret = formUpload(urlStr, textMap, fileMap);
+		log.info("oss上传:"+ret);
+		return path;
+	}
+
+	@SuppressWarnings("rawtypes")
+	private static String formUpload(String urlStr, Map<String, String> textMap, Map<String, String> fileMap) throws IOException {
+		String res = "";
+		HttpURLConnection conn = null;
+		String BOUNDARY = "9431149156168";
+		try {
+			URL url = new URL(urlStr);
+			conn = (HttpURLConnection) url.openConnection();
+			conn.setConnectTimeout(5000);
+			conn.setReadTimeout(30000);
+			conn.setDoOutput(true);
+			conn.setDoInput(true);
+			conn.setRequestMethod("POST");
+			conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
+			conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
+
+			OutputStream out = new DataOutputStream(conn.getOutputStream());
+			// text
+			if (textMap != null) {
+				StringBuffer strBuf = new StringBuffer();
+				Iterator iter = textMap.entrySet().iterator();
+				int i = 0;
+				while (iter.hasNext()) {
+					Map.Entry entry = (Map.Entry) iter.next();
+					String inputName = (String) entry.getKey();
+					String inputValue = (String) entry.getValue();
+					if (inputValue == null) {
+						continue;
+					}
+					if (i == 0) {
+						strBuf.append("--").append(BOUNDARY).append("\r\n");
+						strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
+						strBuf.append(inputValue);
+					} else {
+						strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
+						strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
+
+						strBuf.append(inputValue);
+					}
+
+					i++;
+				}
+				out.write(strBuf.toString().getBytes());
+			}
+
+			// file
+			if (fileMap != null) {
+				Iterator iter = fileMap.entrySet().iterator();
+				while (iter.hasNext()) {
+					Map.Entry entry = (Map.Entry) iter.next();
+					String inputName = (String) entry.getKey();
+					String inputValue = (String) entry.getValue();
+					if (inputValue == null) {
+						continue;
+					}
+					File file = new File(inputValue);
+					String filename = file.getName();
+					String contentType = new MimetypesFileTypeMap().getContentType(file);
+					if (contentType == null || contentType.equals("")) {
+						contentType = "application/octet-stream";
+					}
+
+					StringBuffer strBuf = new StringBuffer();
+					strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
+					strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"; filename=\"" + filename
+							+ "\"\r\n");
+					strBuf.append("Content-Type: " + contentType + "\r\n\r\n");
+
+					out.write(strBuf.toString().getBytes());
+
+					DataInputStream in = new DataInputStream(new FileInputStream(file));
+					int bytes = 0;
+					byte[] bufferOut = new byte[1024];
+					while ((bytes = in.read(bufferOut)) != -1) {
+						out.write(bufferOut, 0, bytes);
+					}
+					in.close();
+				}
+				StringBuffer strBuf = new StringBuffer();
+				out.write(strBuf.toString().getBytes());
+			}
+
+			byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
+			out.write(endData);
+			out.flush();
+			out.close();
+
+			// 读取返回数据
+			StringBuffer strBuf = new StringBuffer();
+			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+			String line = null;
+			while ((line = reader.readLine()) != null) {
+				strBuf.append(line).append("\n");
+			}
+			res = strBuf.toString();
+			reader.close();
+			reader = null;
+		} finally {
+			if (conn != null) {
+				conn.disconnect();
+				conn = null;
+			}
+		}
+		return res;
+	}
+
+	@Override
+	public String saveFile(String siteId, FileStoragePathEnvInfo env, File file) {
+		try {
+			String relativePath=postObject(siteId, env, file, null);
+			AliyunSite as=AliyunSiteManager.getAliyunSite(siteId);
+			return FileStorageType.ALIYUN+"-"+as.getAliyunId()+"://"+relativePath;
+		} catch (IOException e) {
+			throw new StatusException("5001", "上传出错",e);
+		}
+	}
+}

+ 64 - 0
src/main/java/cn/com/qmth/examcloud/web/filestorage/impl/UpyunFileStorageImpl.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.examcloud.web.filestorage.impl;
+
+import java.io.File;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.web.filestorage.FileStorage;
+import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
+import cn.com.qmth.examcloud.web.filestorage.FileStorageType;
+import cn.com.qmth.examcloud.web.filestorage.FileStorageUtil;
+import cn.com.qmth.examcloud.web.upyun.UpYunClient;
+import cn.com.qmth.examcloud.web.upyun.UpYunPathInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunPathEnvironmentInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunService;
+import cn.com.qmth.examcloud.web.upyun.UpyunSite;
+import cn.com.qmth.examcloud.web.upyun.UpyunSiteManager;
+
+@Service(value = "upyunFileStorage")
+public class UpyunFileStorageImpl implements FileStorage {
+	
+	@Autowired
+	private UpyunService upyunService;
+
+	@Override
+	public String saveFile(File file, String path) {
+		String siteId="transPath";
+		UpyunPathEnvironmentInfo env = new UpyunPathEnvironmentInfo();
+		env.setRelativePath(path);
+		String fpath = upyunService.writeFile(siteId, env, file, null).getRelativePath();
+		UpyunSite site=UpyunSiteManager.getUpyunSite(siteId);
+		return FileStorageType.UPYUN+"-"+site.getUpyunId()+"://"+fpath;
+	}
+
+	@Override
+	public String realPath(String path) {
+		String upyunId=FileStorageUtil.getYunId(path);
+		UpYunClient c=UpyunSiteManager.getUpYunClientByUpyunId(upyunId);
+		return FileStorageUtil.getUrl(c.getDomain(), path);
+	}
+
+	@Override
+	public String saveFile(String siteId, FileStoragePathEnvInfo env, File file) {
+		UpYunPathInfo pathInfo =upyunService.writeFile(siteId, of(env), file, null);
+		UpyunSite site=UpyunSiteManager.getUpyunSite(siteId);
+		return FileStorageType.UPYUN+"-"+site.getUpyunId()+"://"+pathInfo.getRelativePath();
+	}
+	
+	private UpyunPathEnvironmentInfo of(FileStoragePathEnvInfo env) {
+		UpyunPathEnvironmentInfo ret=new UpyunPathEnvironmentInfo();
+		ret.setExt1(env.getExt1());
+		ret.setExt2(env.getExt2());
+		ret.setExt3(env.getExt3());
+		ret.setExt4(env.getExt4());
+		ret.setExt5(env.getExt5());
+		ret.setFileSuffix(env.getFileSuffix());
+		ret.setRelativePath(env.getRelativePath());
+		ret.setRootOrgDomain(env.getRootOrgDomain());
+		ret.setRootOrgId(env.getRootOrgId());
+		ret.setTimeMillis(env.getTimeMillis());
+		ret.setUserId(env.getUserId());
+		return ret;
+	}
+}

+ 8 - 0
src/main/java/cn/com/qmth/examcloud/web/upyun/UpYunClient.java

@@ -403,4 +403,12 @@ public class UpYunClient {
 		return "UPYUN " + key + ":" + sign;
 	}
 
+	public String getDomain() {
+		return domain;
+	}
+
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+
 }