WANG 6 år sedan
förälder
incheckning
f2b958cf7d

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

@@ -0,0 +1,282 @@
+package cn.com.qmth.examcloud.web.upyun;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+
+import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
+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.MD5;
+
+/**
+ * upyun client
+ *
+ * @author WANGWEI
+ * @date 2018年11月21日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UpYunClient {
+
+	protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+
+	/**
+	 * 空间名
+	 */
+	protected String bucketName = null;
+
+	/**
+	 * 操作员名
+	 */
+	protected String userName = null;
+
+	/**
+	 * 操作员密码
+	 */
+	protected String password = null;
+
+	protected String md5Password = null;
+
+	public static final String API_DOMAIN = "v0.api.upyun.com";
+
+	private static final String MKDIR = "mkdir";
+
+	private final String METHOD_PUT = "PUT";
+
+	private final String METHOD_DELETE = "DELETE";
+
+	private final String DATE = "Date";
+
+	private final String AUTHORIZATION = "Authorization";
+
+	private final String SEPARATOR = "/";
+
+	private CloseableHttpClient httpclient;
+
+	private RequestConfig requestConfig;
+
+	private String domain;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param bucketName
+	 * @param userName
+	 * @param password
+	 */
+	public UpYunClient(String bucketName, String userName, String password, String domain) {
+		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
+		cm.setMaxTotal(1000);
+		httpclient = HttpClients.custom().setConnectionManager(cm).disableAutomaticRetries()
+				.build();
+		requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
+				.setSocketTimeout(10000).setConnectTimeout(10000).build();
+
+		this.bucketName = bucketName;
+		this.userName = userName;
+		this.password = password;
+		this.domain = domain;
+		this.md5Password = MD5.encrypt32(password);
+	}
+
+	/**
+	 * 上传文件
+	 *
+	 * @author WANGWEI
+	 * @param filePath
+	 * @param file
+	 * @return
+	 */
+	public UpYunPathInfo writeFile(String filePath, File file) {
+
+		InputStream in = null;
+		try {
+			in = new FileInputStream(file);
+			return writeFile(filePath, in);
+		} catch (FileNotFoundException e) {
+			throw new ExamCloudRuntimeException(e);
+		} finally {
+			IOUtils.closeQuietly(in);
+		}
+	}
+
+	/**
+	 * 上传文件
+	 *
+	 * @author WANGWEI
+	 * @param filePath
+	 * @param in
+	 * @return
+	 */
+	public UpYunPathInfo writeFile(String filePath, InputStream in) {
+		String path = formatPath(filePath);
+		String url = "https://" + API_DOMAIN + path;
+
+		HttpPut httpPut = new HttpPut(url);
+		httpPut.setConfig(this.requestConfig);
+		CloseableHttpResponse response = null;
+
+		long s = System.currentTimeMillis();
+		try {
+
+			String date = getDate();
+			String authorization = sign(userName, md5Password, METHOD_PUT, path, date, "", "");
+			httpPut.addHeader(AUTHORIZATION, authorization);
+			httpPut.addHeader(DATE, date);
+			httpPut.addHeader(MKDIR, "true");
+
+			httpPut.setEntity(new InputStreamEntity(in));
+			response = httpclient.execute(httpPut);
+			int statusCode = response.getStatusLine().getStatusCode();
+
+			if (HttpStatus.SC_OK != statusCode) {
+				log.error("[upyun error] " + EntityUtils.toString(response.getEntity(), "UTF-8"));
+				throw new StatusException("100001", "[upyun]. fail to write file");
+			}
+		} catch (StatusException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new ExamCloudRuntimeException(e);
+		} finally {
+			IOUtils.closeQuietly(in);
+			IOUtils.closeQuietly(response);
+			httpPut.releaseConnection();
+		}
+
+		if (log.isDebugEnabled()) {
+			log.debug("[upyun]. write file. path=" + path + "; cost "
+					+ (System.currentTimeMillis() - s) + " ms.");
+		}
+
+		String fileUrl = this.domain + filePath;
+		return new UpYunPathInfo(fileUrl, filePath);
+	}
+
+	/**
+	 * 删除文件
+	 *
+	 * @author WANGWEI
+	 * @param filePath
+	 * @return
+	 */
+	public void deleteFile(String filePath) {
+		String path = formatPath(filePath);
+		String url = "https://" + API_DOMAIN + path;
+
+		HttpDelete httpDelete = new HttpDelete(url);
+		httpDelete.setConfig(this.requestConfig);
+		CloseableHttpResponse response = null;
+
+		long s = System.currentTimeMillis();
+		try {
+			String date = getDate();
+			String authorization = sign(userName, md5Password, METHOD_DELETE, path, date, "", "");
+			httpDelete.addHeader(AUTHORIZATION, authorization);
+			httpDelete.addHeader(DATE, date);
+
+			response = httpclient.execute(httpDelete);
+			int statusCode = response.getStatusLine().getStatusCode();
+
+			if (HttpStatus.SC_OK != statusCode) {
+				log.error("[upyun error] " + EntityUtils.toString(response.getEntity(), "UTF-8"));
+				throw new StatusException("100002", "[upyun]. fail to delete file");
+			}
+		} catch (StatusException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new ExamCloudRuntimeException(e);
+		} finally {
+			IOUtils.closeQuietly(response);
+			httpDelete.releaseConnection();
+		}
+
+		if (log.isDebugEnabled()) {
+			log.debug("[upyun]. delete file. path=" + path + "; cost "
+					+ (System.currentTimeMillis() - s) + " ms.");
+		}
+
+	}
+
+	/**
+	 * 格式化路径参数
+	 * <p>
+	 * 最终构成的格式:"/空间名/文件路径"
+	 *
+	 * @param path
+	 *            目录路径或文件路径
+	 * @return 格式化后的路径
+	 */
+	private String formatPath(String path) {
+
+		if (StringUtils.isNotBlank(path)) {
+
+			// 去除前后的空格
+			path = path.trim();
+
+			// 确保路径以"/"开头
+			if (!path.startsWith(SEPARATOR)) {
+				return SEPARATOR + bucketName + SEPARATOR + path;
+			}
+		}
+
+		return SEPARATOR + bucketName + path;
+	}
+
+	private String getDate() {
+		Calendar calendar = Calendar.getInstance();
+		SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",
+				Locale.US);
+		dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+		return dateFormat.format(calendar.getTime());
+	}
+
+	private byte[] hashHmac(String data, String key)
+			throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
+		SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
+		Mac mac = Mac.getInstance("HmacSHA1");
+		mac.init(signingKey);
+		return mac.doFinal(data.getBytes());
+	}
+
+	private String sign(String key, String secret, String method, String uri, String date,
+			String policy, String md5) throws Exception {
+		String value = method + "&" + uri + "&" + date;
+		if (policy != null && policy.length() > 0) {
+			value = value + "&" + policy;
+		}
+
+		if (md5 != null && md5.length() > 0) {
+			value = value + "&" + md5;
+		}
+		byte[] hmac = hashHmac(value, secret);
+		String sign = Base64.getEncoder().encodeToString(hmac);
+		return "UPYUN " + key + ":" + sign;
+	}
+
+}

+ 44 - 0
src/main/java/cn/com/qmth/examcloud/web/upyun/UpYunPathInfo.java

@@ -0,0 +1,44 @@
+package cn.com.qmth.examcloud.web.upyun;
+
+/**
+ * 路径
+ *
+ * @author WANGWEI
+ * @date 2019年5月9日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UpYunPathInfo {
+
+	private String url;
+
+	private String relativePath;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param url
+	 * @param relativePath
+	 */
+	public UpYunPathInfo(String url, String relativePath) {
+		super();
+		this.url = url;
+		this.relativePath = relativePath;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public String getRelativePath() {
+		return relativePath;
+	}
+
+	public void setRelativePath(String relativePath) {
+		this.relativePath = relativePath;
+	}
+
+}

+ 66 - 0
src/main/java/cn/com/qmth/examcloud/web/upyun/UpyunSite.java

@@ -0,0 +1,66 @@
+package cn.com.qmth.examcloud.web.upyun;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年11月21日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UpyunSite implements JsonSerializable {
+
+	private static final long serialVersionUID = -1754474062438702321L;
+
+	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;
+	}
+
+}

+ 113 - 0
src/main/java/cn/com/qmth/examcloud/web/upyun/UpyunSiteManager.java

@@ -0,0 +1,113 @@
+package cn.com.qmth.examcloud.web.upyun;
+
+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;
+
+/**
+ * upyun site manager
+ *
+ * @author WANGWEI
+ * @date 2018年11月21日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UpyunSiteManager {
+
+	private static final Map<String, UpYunClient> CLIENT_HOLDERS = new ConcurrentHashMap<>();
+
+	private static final Map<String, UpyunSite> SITE_HOLDERS = new ConcurrentHashMap<>();
+
+	public static void init() {
+		String resoucePath = PathUtil.getResoucePath("upyun.xml");
+		File file = new File(resoucePath);
+
+		XStream xStream = XStreamBuilder.newInstance().build();
+		xStream.allowTypes(new Class[]{UpyunSite.class, List.class});
+		xStream.alias("sites", List.class);
+		xStream.alias("site", UpyunSite.class);
+
+		List<UpyunSite> list = null;
+		try {
+			@SuppressWarnings("unchecked")
+			List<UpyunSite> obj = (List<UpyunSite>) xStream.fromXML(file);
+			list = obj;
+		} catch (Exception e) {
+			throw new StatusException("520001", "upyun.xml is wrong", e);
+		}
+
+		for (UpyunSite upyunSite : list) {
+
+			SITE_HOLDERS.put(upyunSite.getId(), upyunSite);
+
+			String upyunId = upyunSite.getUpyunId();
+			String bucketName = PropertyHolder.getString("$upyun.site." + upyunId + ".bucketName");
+			String userName = PropertyHolder.getString("$upyun.site." + upyunId + ".userName");
+			String password = PropertyHolder.getString("$upyun.site." + upyunId + ".password");
+			String domain = PropertyHolder.getString("$upyun.site." + upyunId + ".domain");
+
+			if (StringUtils.isBlank(bucketName)) {
+				throw new StatusException("520002",
+						"bucketName is not configured. upyunId=" + upyunId);
+			}
+			if (StringUtils.isBlank(userName)) {
+				throw new StatusException("520003",
+						"userName is not configured. upyunId=" + upyunId);
+			}
+			if (StringUtils.isBlank(password)) {
+				throw new StatusException("520004",
+						"password is not configured. upyunId=" + upyunId);
+			}
+			if (StringUtils.isBlank(domain)) {
+				throw new StatusException("520004", "domain is not configured. upyunId=" + upyunId);
+			}
+
+			UpYunClient upYunClient = new UpYunClient(bucketName, userName, password, domain);
+
+			CLIENT_HOLDERS.put(upyunSite.getId(), upYunClient);
+		}
+
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param siteId
+	 * @return
+	 */
+	public static UpYunClient getUpYunClient(String siteId) {
+		UpYunClient upYunClient = CLIENT_HOLDERS.get(siteId);
+
+		if (null == upYunClient) {
+			throw new StatusException("520005", "upYunClient is null");
+		}
+		return upYunClient;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param siteId
+	 * @return
+	 */
+	public static UpyunSite getUpyunSite(String siteId) {
+		UpyunSite upyunSite = SITE_HOLDERS.get(siteId);
+
+		if (null == upyunSite) {
+			throw new StatusException("520006", "upyunSite is null");
+		}
+		return upyunSite;
+	}
+
+}