|
@@ -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;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|