需求背景
目前公司内部项目所支持的文件云存储方式还是公司内部项目组提供的方案,但在时间的考验之下,弊端显现,尤其是灾备切换过程中需要切换访问地址,这种操作不方便,更可能因为中间过程的失误导致资源不可用,而且这种操作也会带来资源可用的延时,仔细斟酌发现这种操作似乎并不合逻辑。
在众多项目组的千呼万唤之下,阿里云的OSS存储方案进入了我们的视线,依托公共云环境,屏蔽项目本身切换状态可能带来的资源不可用的问题,将资源存储与访问独立出来,极大的与企业自有项目解耦,同时降低企业自身项目运行状态对公共资源访问的影响。
项目接入
Captain的本项目基于JDK1.8,采用SpringBoot框架开发。
前期准备
从阿里云平台获取到endpoint、accessKeyId、accessKeySecret相关配置信息,因为只有在这些信息的支持下,才能展开后续功能的开发。
创建bucket(存储命名空间、平台唯一才行),可以在阿里云操作台建立,也可以通过代码生成。
Maven依赖
1 <dependency> 2 <groupId>com.aliyun.oss</groupId> 3 <artifactId>aliyun-sdk-oss</artifactId> 4 <version>2.8.3</version> 5 </dependency>
属性配置
可以将和环境相关的固定配置类信息放置于properties文件中:
# aliyun oss aliyun.oss.endpoint="http://oss-cn-hangzhou.aliyuncs.com" aliyun.oss.keyid=<yourAccessKeyId> aliyun.oss.keysecret=<yourAccessKeySecret> aliyun.oss.bucketname=<yourBucketName> # aliyun filehost based on dev/test/prod(tha/idn) aliyun.oss.filehost=dev
编写工具类
1 /** 2 * @Project: captain-supply-chain 3 * @PackageName: com.captain.supply-chain.common.file 4 * @Author: Captain&D 5 * @cnblogs: https://www.cnblogs.com/captainad 6 * @DateTime: 2019/5/10 18:12. 7 * @Description: Upload file to Aliyun OSS. 8 */ 9 @Slf4j 10 @Component 11 public class AliyunOssService { 12 13 /** 14 * 斜杠 15 */ 16 private final String FLAG_SLANTING_ROD = "/"; 17 /** 18 * http:// 19 */ 20 private final String FLAG_HTTP = "http://"; 21 /** 22 * https:// 23 */ 24 private final String FLAG_HTTPS = "https://"; 25 /** 26 * 空字符串 27 */ 28 private final String FLAG_EMPTY_STRING = ""; 29 /** 30 * 点号 31 */ 32 private final String FLAG_DOT = "."; 33 /** 34 * 横杠 35 */ 36 private final String FLAG_CROSSBAR = "-"; 37 38 /** 39 * 缺省的最大上传文件大小:20M 40 */ 41 private final int DEFAULT_MAXIMUM_FILE_SIZE = 20; 42 43 /** 44 * endpoint 45 */ 46 @Value("${aliyun.oss.endpoint}") 47 private String endpoint; 48 49 /** 50 * access key id 51 */ 52 @Value("${aliyun.oss.keyid}") 53 private String accessKeyId; 54 55 /** 56 * access key secret 57 */ 58 @Value("${aliyun.oss.keysecret}") 59 private String accessKeySecret; 60 61 /** 62 * bucket name (namespace) 63 */ 64 @Value("${aliyun.oss.bucketname}") 65 private String bucketName; 66 67 /** 68 * file host (dev/test/prod) 69 */ 70 @Value("${aliyun.oss.filehost}") 71 private String fileHost; 72 73 @Autowired 74 protected GetSetCacheService getSetCacheService; 75 76 /** 77 * 以文件流的方式上传文件 78 * @Author: Captain&D 79 * @cnblogs: https://www.cnblogs.com/captainad 80 * @param fileName 文件名称 81 * @param filePath 文件路径 82 * @param inputStream 文件输入流 83 * @return 84 */ 85 public String uploadFile(String fileName, String filePath, InputStream inputStream) { 86 return coreUpload(fileName, filePath, inputStream); 87 } 88 89 /** 90 * 核心上传功能 91 * @Author: Captain&D 92 * @cnblogs: https://www.cnblogs.com/captainad 93 * @param fileName 文件名 94 * @param filePath 文件路径 95 * @param inputStream 文件输入流 96 * @return 97 */ 98 private String coreUpload(String fileName, String filePath, InputStream inputStream) { 99 log.info("Start to upload file...."); 100 if(StringUtils.isEmpty(fileName) || inputStream == null) { 101 log.error("Filename Or inputStream is lack when upload file."); 102 return null; 103 } 104 if(StringUtils.isEmpty(filePath)) { 105 log.warn("File path is lack when upload file but we automatically generated"); 106 String dateCategory = DateUtil.getFormatDate(new Date(), "yyyyMMdd"); 107 filePath = FLAG_SLANTING_ROD.concat(dateCategory).concat(FLAG_SLANTING_ROD); 108 } 109 String fileUrl; 110 OSSClient ossClient = null; 111 try{ 112 113 // If the upload file size exceeds the limit 114 long maxSizeAllowed = getMaximumFileSizeAllowed(); 115 if(Long.valueOf(inputStream.available()) > maxSizeAllowed) { 116 log.error("Uploaded file is too big."); 117 return null; 118 } 119 120 // Create OSS instance 121 ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret); 122 123 // Create bucket if not exists 124 if (!ossClient.doesBucketExist(bucketName)) { 125 log.info("Bucket '{}' is not exists and create it now.", bucketName); 126 ossClient.createBucket(bucketName); 127 CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); 128 createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead); 129 ossClient.createBucket(createBucketRequest); 130 } 131 132 /*********************************/ 133 // List the bucket in my account 134 //listBuckets(ossClient); 135 /*********************************/ 136 137 // File path format 138 if(!filePath.startsWith(FLAG_SLANTING_ROD)) { 139 filePath = FLAG_SLANTING_ROD.concat(filePath); 140 } 141 if(!filePath.endsWith(FLAG_SLANTING_ROD)) { 142 filePath = filePath.concat(FLAG_SLANTING_ROD); 143 } 144 145 // File url 146 StringBuilder buffer = new StringBuilder(); 147 buffer.append(fileHost).append(filePath).append(fileName); 148 fileUrl = buffer.toString(); 149 log.info("After format the file url is {}", fileUrl); 150 151 // Upload file and set ACL 152 PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName, fileUrl, inputStream)); 153 ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead); 154 if(result != null) { 155 log.info("Upload result:{}", result.getETag()); 156 log.info("Upload file {} successfully.", fileName); 157 } 158 fileUrl = getHostUrl().concat(fileUrl); 159 log.info("Call path is {}", fileUrl); 160 161 /***********************************/ 162 // List objects in your bucket 163 //listObjects(ossClient); 164 /***********************************/ 165 166 }catch (Exception e){ 167 log.error("Upload file failed.", e); 168 fileUrl = null; 169 }finally { 170 if(ossClient != null) { 171 ossClient.shutdown(); 172 } 173 } 174 return fileUrl; 175 } 176 177 /** 178 * 列出buckets下的所有文件 179 * @Author: Captain&D 180 * @cnblogs: https://www.cnblogs.com/captainad 181 * @param ossClient 182 */ 183 private void listObjects(OSSClient ossClient) { 184 System.out.println("Listing objects"); 185 ObjectListing objectListing = ossClient.listObjects(bucketName); 186 for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) { 187 System.out.println(" - " + objectSummary.getKey() + " " + 188 "(size = " + objectSummary.getSize() + ")"); 189 } 190 System.out.println(); 191 } 192 193 /** 194 * 列出当前用户下的所有bucket 195 * @Author: Captain&D 196 * @cnblogs: https://www.cnblogs.com/captainad 197 * @param ossClient 198 */ 199 private void listBuckets(OSSClient ossClient) { 200 System.out.println("Listing buckets"); 201 ListBucketsRequest listBucketsRequest = new ListBucketsRequest(); 202 listBucketsRequest.setMaxKeys(500); 203 for (Bucket bucket : ossClient.listBuckets()) { 204 System.out.println(" - " + bucket.getName()); 205 } 206 System.out.println(); 207 } 208 209 /** 210 * 以文件的形式上传文件 211 * @Author: Captain&D 212 * @cnblogs: https://www.cnblogs.com/captainad 213 * @param fileName 214 * @param filePath 215 * @param file 216 * @return 217 */ 218 public String uploadFile(String fileName, String filePath, File file) { 219 if(file == null) { 220 log.warn("File is lack when upload."); 221 return null; 222 } 223 if(StringUtils.isEmpty(fileName)) { 224 log.warn("File name is lack when upload file but we automatically generated"); 225 String uuidFileName = UUID.randomUUID().toString().replace(FLAG_CROSSBAR, FLAG_EMPTY_STRING); 226 String fname = file.getName(); 227 String suffix = fname.substring(fname.lastIndexOf(FLAG_DOT), fname.length()); 228 fileName = uuidFileName.concat(suffix); 229 } 230 InputStream inputStream = null; 231 String fileUrl = null; 232 try{ 233 inputStream = new FileInputStream(file); 234 fileUrl = uploadFile(fileName, filePath, inputStream); 235 }catch (Exception e){ 236 log.error("Upload file error.", e); 237 }finally { 238 IOUtils.safeClose(inputStream); 239 } 240 return fileUrl; 241 } 242 243 /** 244 * 获取访问的base地址 245 * @Author: Captain&D 246 * @cnblogs: https://www.cnblogs.com/captainad 247 * @return 248 */ 249 private String getHostUrl() { 250 String hostUrl = null; 251 if(this.endpoint.startsWith(FLAG_HTTP)) { 252 hostUrl = FLAG_HTTP.concat(this.bucketName).concat(FLAG_DOT) 253 .concat(this.endpoint.replace(FLAG_HTTP, FLAG_EMPTY_STRING)).concat(FLAG_SLANTING_ROD); 254 } else if (this.endpoint.startsWith(FLAG_HTTPS)) { 255 return FLAG_HTTPS.concat(this.bucketName).concat(FLAG_DOT) 256 .concat(this.endpoint.replace(FLAG_HTTPS, FLAG_EMPTY_STRING)).concat(FLAG_SLANTING_ROD); 257 } 258 return hostUrl; 259 } 260 261 /** 262 * 获取最大允许上传文件的大小 263 * @Author: Captain&D 264 * @cnblogs: https://www.cnblogs.com/captainad 265 * @return 266 */ 267 private long getMaximumFileSizeAllowed() { 268 // 缓存单位是M 269 String maxConfigVal = getSetCacheService.getConfigValue("upload_maximum_file_size"); 270 if(StringUtils.isEmpty(maxConfigVal)) { 271 return DEFAULT_MAXIMUM_FILE_SIZE * 1024L * 1024L; 272 }else { 273 return Long.valueOf(maxConfigVal.trim()) * 1024L * 1024L; 274 } 275 } 276 277 /** 278 * 删除文件 279 * @Author: Captain&D 280 * @cnblogs: https://www.cnblogs.com/captainad 281 * @param fileUrl 文件访问的全路径 282 */ 283 public void deleteFile(String fileUrl) { 284 log.info("Start to delete file from OSS.{}", fileUrl); 285 if(StringUtils.isEmpty(fileUrl) 286 || (!fileUrl.startsWith(FLAG_HTTP) 287 && !fileUrl.startsWith(FLAG_HTTPS))) { 288 log.error("Delete file failed because the invalid file address. -> {}", fileUrl); 289 return; 290 } 291 OSSClient ossClient = null; 292 try{ 293 /** 294 * http:// bucketname dev/test/pic/abc.jpg = key 295 * http:// captainad.oss-ap-southeast-1.aliyuncs.com/dev/test/pic/abc.jpg 296 */ 297 String key = fileUrl.replace(getHostUrl(), FLAG_EMPTY_STRING); 298 if(log.isDebugEnabled()) { 299 log.debug("Delete file key is {}", key); 300 } 301 ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret); 302 ossClient.deleteObject(bucketName, key); 303 }catch (Exception e){ 304 log.error("Delete file error.", e); 305 } finally { 306 if(ossClient != null) { 307 ossClient.shutdown(); 308 } 309 } 310 } 311 312 }
参考资料
1、https://help.aliyun.com/document_detail/32008.html