UpYunClient.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. package cn.com.qmth.examcloud.web.upyun;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.InputStream;
  6. import java.security.InvalidKeyException;
  7. import java.security.NoSuchAlgorithmException;
  8. import java.security.SignatureException;
  9. import java.text.SimpleDateFormat;
  10. import java.util.Base64;
  11. import java.util.Calendar;
  12. import java.util.Locale;
  13. import java.util.Map;
  14. import java.util.TimeZone;
  15. import java.util.concurrent.TimeUnit;
  16. import javax.crypto.Mac;
  17. import javax.crypto.spec.SecretKeySpec;
  18. import org.apache.commons.compress.utils.IOUtils;
  19. import org.apache.commons.lang3.StringUtils;
  20. import org.apache.http.HttpStatus;
  21. import org.apache.http.client.config.RequestConfig;
  22. import org.apache.http.client.methods.CloseableHttpResponse;
  23. import org.apache.http.client.methods.HttpDelete;
  24. import org.apache.http.client.methods.HttpPut;
  25. import org.apache.http.entity.InputStreamEntity;
  26. import org.apache.http.impl.client.CloseableHttpClient;
  27. import org.apache.http.impl.client.HttpClients;
  28. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  29. import org.apache.http.util.EntityUtils;
  30. import com.google.common.collect.Maps;
  31. import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
  32. import cn.com.qmth.examcloud.commons.exception.StatusException;
  33. import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
  34. import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
  35. import cn.com.qmth.examcloud.commons.util.MD5;
  36. /**
  37. * upyun client
  38. *
  39. * @author WANGWEI
  40. * @date 2018年11月21日
  41. * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  42. */
  43. public class UpYunClient {
  44. protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
  45. /**
  46. * 空间名
  47. */
  48. protected String bucketName = null;
  49. /**
  50. * 操作员名
  51. */
  52. protected String userName = null;
  53. /**
  54. * 操作员密码
  55. */
  56. protected String password = null;
  57. protected String md5Password = null;
  58. public static final String API_DOMAIN = "v0.api.upyun.com";
  59. private static final String MKDIR = "mkdir";
  60. private final String METHOD_PUT = "PUT";
  61. private final String METHOD_DELETE = "DELETE";
  62. private final String DATE = "Date";
  63. private final String AUTHORIZATION = "Authorization";
  64. private final String SEPARATOR = "/";
  65. private static CloseableHttpClient httpclient;
  66. private static RequestConfig requestConfig;
  67. private String domain;
  68. static {
  69. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(10,
  70. TimeUnit.SECONDS);
  71. cm.setValidateAfterInactivity(2000);
  72. cm.setMaxTotal(1000);
  73. cm.setDefaultMaxPerRoute(1000);
  74. httpclient = HttpClients.custom().setConnectionManager(cm).disableAutomaticRetries()
  75. .build();
  76. requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
  77. .setSocketTimeout(10000).setConnectTimeout(10000).build();
  78. }
  79. /**
  80. * 构造函数
  81. *
  82. * @param bucketName
  83. * @param userName
  84. * @param password
  85. */
  86. public UpYunClient(String bucketName, String userName, String password, String domain) {
  87. this.bucketName = bucketName;
  88. this.userName = userName;
  89. this.password = password;
  90. this.domain = domain;
  91. this.md5Password = MD5.encrypt32(password);
  92. }
  93. /**
  94. * 创建又拍云签名
  95. *
  96. * @author WANGWEI
  97. * @param filePath
  98. * @return
  99. */
  100. public UpYunSign buildUpYunSign(String filePath) {
  101. String path = formatPath(filePath);
  102. String url = "https://" + API_DOMAIN + path;
  103. Map<String, String> headers = Maps.newHashMap();
  104. String date = getDate();
  105. String authorization = null;
  106. try {
  107. authorization = sign(userName, md5Password, METHOD_PUT, path, date, "", "");
  108. } catch (Exception e) {
  109. throw new StatusException("100005", "[upyun]. fail to build sign", e);
  110. }
  111. headers.put(AUTHORIZATION, authorization);
  112. headers.put(DATE, date);
  113. headers.put(MKDIR, "true");
  114. UpYunSign sign = new UpYunSign();
  115. sign.setUrl(url);
  116. sign.setHeaders(headers);
  117. return sign;
  118. }
  119. /**
  120. * 上传文件
  121. *
  122. * @author WANGWEI
  123. * @param filePath
  124. * @param file
  125. * @return
  126. */
  127. public UpYunPathInfo writeFile(String filePath, File file) {
  128. InputStream in = null;
  129. try {
  130. in = new FileInputStream(file);
  131. return writeFile(filePath, in);
  132. } catch (FileNotFoundException e) {
  133. throw new ExamCloudRuntimeException(e);
  134. } finally {
  135. IOUtils.closeQuietly(in);
  136. }
  137. }
  138. /**
  139. * 上传文件
  140. *
  141. * @author WANGWEI
  142. * @param filePath
  143. * @param in
  144. * @return
  145. */
  146. public UpYunPathInfo writeFile(String filePath, InputStream in) {
  147. String path = formatPath(filePath);
  148. String url = "https://" + API_DOMAIN + path;
  149. HttpPut httpPut = new HttpPut(url);
  150. httpPut.setConfig(UpYunClient.requestConfig);
  151. CloseableHttpResponse response = null;
  152. long s = System.currentTimeMillis();
  153. try {
  154. String date = getDate();
  155. String authorization = sign(userName, md5Password, METHOD_PUT, path, date, "", "");
  156. httpPut.addHeader(AUTHORIZATION, authorization);
  157. httpPut.addHeader(DATE, date);
  158. httpPut.addHeader(MKDIR, "true");
  159. httpPut.setEntity(new InputStreamEntity(in));
  160. response = httpclient.execute(httpPut);
  161. int statusCode = response.getStatusLine().getStatusCode();
  162. if (HttpStatus.SC_OK != statusCode) {
  163. log.error("[upyun error] " + EntityUtils.toString(response.getEntity(), "UTF-8"));
  164. throw new StatusException("100001", "[upyun]. fail to write file");
  165. }
  166. } catch (StatusException e) {
  167. throw e;
  168. } catch (Exception e) {
  169. throw new ExamCloudRuntimeException(e);
  170. } finally {
  171. IOUtils.closeQuietly(in);
  172. IOUtils.closeQuietly(response);
  173. httpPut.releaseConnection();
  174. }
  175. if (log.isDebugEnabled()) {
  176. log.debug("[upyun]. write file. path=" + path + "; cost "
  177. + (System.currentTimeMillis() - s) + " ms.");
  178. }
  179. String fileUrl = this.domain + filePath;
  180. return new UpYunPathInfo(fileUrl, filePath);
  181. }
  182. /**
  183. * 删除文件
  184. *
  185. * @author WANGWEI
  186. * @param filePath
  187. * @return
  188. */
  189. public void deleteFile(String filePath) {
  190. String path = formatPath(filePath);
  191. String url = "https://" + API_DOMAIN + path;
  192. HttpDelete httpDelete = new HttpDelete(url);
  193. httpDelete.setConfig(UpYunClient.requestConfig);
  194. CloseableHttpResponse response = null;
  195. long s = System.currentTimeMillis();
  196. try {
  197. String date = getDate();
  198. String authorization = sign(userName, md5Password, METHOD_DELETE, path, date, "", "");
  199. httpDelete.addHeader(AUTHORIZATION, authorization);
  200. httpDelete.addHeader(DATE, date);
  201. response = httpclient.execute(httpDelete);
  202. int statusCode = response.getStatusLine().getStatusCode();
  203. if (HttpStatus.SC_OK != statusCode) {
  204. log.error("[upyun error] " + EntityUtils.toString(response.getEntity(), "UTF-8"));
  205. throw new StatusException("100002", "[upyun]. fail to delete file");
  206. }
  207. } catch (StatusException e) {
  208. throw e;
  209. } catch (Exception e) {
  210. throw new ExamCloudRuntimeException(e);
  211. } finally {
  212. IOUtils.closeQuietly(response);
  213. httpDelete.releaseConnection();
  214. }
  215. if (log.isDebugEnabled()) {
  216. log.debug("[upyun]. delete file. path=" + path + "; cost "
  217. + (System.currentTimeMillis() - s) + " ms.");
  218. }
  219. }
  220. /**
  221. * 格式化路径参数
  222. * <p>
  223. * 最终构成的格式:"/空间名/文件路径"
  224. *
  225. * @param path
  226. * 目录路径或文件路径
  227. * @return 格式化后的路径
  228. */
  229. private String formatPath(String path) {
  230. if (StringUtils.isNotBlank(path)) {
  231. // 去除前后的空格
  232. path = path.trim();
  233. // 确保路径以"/"开头
  234. if (!path.startsWith(SEPARATOR)) {
  235. return SEPARATOR + bucketName + SEPARATOR + path;
  236. }
  237. }
  238. return SEPARATOR + bucketName + path;
  239. }
  240. private String getDate() {
  241. Calendar calendar = Calendar.getInstance();
  242. SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",
  243. Locale.US);
  244. dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
  245. return dateFormat.format(calendar.getTime());
  246. }
  247. private byte[] hashHmac(String data, String key)
  248. throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
  249. SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
  250. Mac mac = Mac.getInstance("HmacSHA1");
  251. mac.init(signingKey);
  252. return mac.doFinal(data.getBytes());
  253. }
  254. private String sign(String key, String secret, String method, String uri, String date,
  255. String policy, String md5) throws Exception {
  256. String value = method + "&" + uri + "&" + date;
  257. if (policy != null && policy.length() > 0) {
  258. value = value + "&" + policy;
  259. }
  260. if (md5 != null && md5.length() > 0) {
  261. value = value + "&" + md5;
  262. }
  263. byte[] hmac = hashHmac(value, secret);
  264. String sign = Base64.getEncoder().encodeToString(hmac);
  265. return "UPYUN " + key + ":" + sign;
  266. }
  267. }