崩潰!我?guī)У膶?shí)習(xí)生竟然把圖片直接存到了服務(wù)器上!
小二是新來的實(shí)習(xí)生,作為技術(shù) leader,我給他安排了一個(gè)非常簡單的練手任務(wù),把前端 markdown 編輯器里上傳的圖片保存到服務(wù)器端,結(jié)果他真的就把圖片直接保存到了服務(wù)器上,這下可把我氣壞了,就不能搞個(gè)對象存儲服務(wù),比如說 OSS、MinIO?
他理直氣壯地反駁道:“誰讓你不講清楚,我去找老板把你開掉!”我瞬間就慫了,說,“來來來,我手把手教你怎么把圖片保存到 OSS 上,好不好?”
“不用了,還是我來教你吧?!毙《浅W孕?,下面是他在 Spring Boot 應(yīng)用中整合 OSS 做的記錄。
一、開通 OSS
OSS 也就是 Object Storage Service,是阿里云提供的一套對象存儲服務(wù),國內(nèi)的競品還有七牛云的 Kodo和騰訊云的COS。
第一步,登錄阿里云官網(wǎng),搜索“OSS”關(guān)鍵字,進(jìn)入 OSS 產(chǎn)品頁。
第二步,如果是 OSS 新用戶的話,可以享受 6 個(gè)月的新人專享優(yōu)惠價(jià),不過續(xù)費(fèi)的時(shí)候還是會肉疼。
第三步,進(jìn)入 OSS 管理控制臺,點(diǎn)擊「Bucket 列表」,點(diǎn)擊「創(chuàng)建 Bucket」。

Bucket 的詞面意思是桶,這里指存儲空間,就是用于存儲對象的容器。注意讀寫權(quán)限為“公共讀”,也就是允許互聯(lián)網(wǎng)用戶訪問云空間上的圖片。
第四步,點(diǎn)擊「確定」就算是開通成功了。
二、整合 OSS
第一步,在 pom.xml 文件中添加 OSS 的依賴。
????com.aliyun.oss
????aliyun-sdk-oss
????3.10.2
第二步,在 application.yml 文件中添加 OSS 配置項(xiàng)。
aliyun:
??oss:
??????#?oss對外服務(wù)的訪問域名
????endpoint:?oss-cn-beijing.aliyuncs.com
??????#?訪問身份驗(yàn)證中用到用戶標(biāo)識
????accessKeyId:?LTAI5
??????#?用戶用于加密簽名字符串和oss用來驗(yàn)證簽名字符串的密鑰
????accessKeySecret:?RYN
??????#?oss的存儲空間
????bucketName:?itwanger-oss1
??????#?上傳文件大小(M)
????maxSize:?3
??????#?上傳文件夾路徑前綴
????dir:
??????prefix:?codingmore/images/
第三步,新增 OssClientConfig.java 配置類,主要就是通過 ?@Value 注解從配置文件中獲取配置項(xiàng),然后創(chuàng)建 OSSClient。
@Configuration
public?class?OssClientConfig?{
????@Value("${aliyun.oss.endpoint}")
????String?endpoint?;
????@Value("${aliyun.oss.accessKeyId}")
????String?accessKeyId?;
????@Value("${aliyun.oss.accessKeySecret}")
????String?accessKeySecret;
????@Bean
????public?OSSClient?createOssClient()?{
????????return?(OSSClient)new?OSSClientBuilder().build(endpoint,?accessKeyId,?accessKeySecret);
????}
}
第四步,新增文件上傳接口 OssController.java,參數(shù)為 MultipartFile。
@Controller
@Api(tags?=?"上傳")
@RequestMapping("/ossController")
public?class?OssController?{
????@Autowired
????private?IOssService?ossService;
????@RequestMapping(value?=?"/upload",method=RequestMethod.POST)
????@ResponseBody
????@ApiOperation("上傳")
????public?ResultObject?upload(@RequestParam("file")?MultipartFile?file,?HttpServletRequest?req)?? {
????????return?ResultObject.success(ossService.upload(file));
????}
}
第五步,新增 Service,將文件上傳到 OSS,并返回文件保存路徑。
@Service
public?class?OssServiceImpl?implements?IOssService{
????@Value("${aliyun.oss.maxSize}")
????private?int?maxSize;
???
????@Value("${aliyun.oss.bucketName}")
????private?String?bucketName;
??
????@Value("${aliyun.oss.dir.prefix}")
????private?String?dirPrefix;
????
????@Autowired
????private?OSSClient?ossClient;???
????@Override
????public?String?upload(MultipartFile?file)?{
????????try?{
????????????return?upload(file.getInputStream(),?file.getOriginalFilename());
????????}?catch?(IOException?e)?{
????????????LOGGER.error(e.getMessage());
????????}
????????return?null;
????}
????@Override
????public?String?upload(InputStream?inputStream,String?name)?{
????????String?objectName?=?getBucketName(name);
????????//?創(chuàng)建PutObject請求。
????????ossClient.putObject(bucketName,?objectName,?inputStream);
????????return?formatPath(objectName);
????}
????private?String?getBucketName(String?url){
????????String?ext?=?"";
????????for(String?extItem:imageExtension){
????????????if(url.indexOf(extItem)?!=?-1){
????????????????ext?=?extItem;
????????????????break;
????????????}
????????}
????????return?dirPrefix+?DateUtil.today()+"/"+?IdUtil.randomUUID()+ext;
????}
????private?String?formatPath(String?objectName){
????????return?"https://"??+bucketName+"."+?ossClient.getEndpoint().getHost()?+?"/"?+?objectName;
????}
}??
第六步,打開 Apipost,測試 OSS 上傳接口,注意參數(shù)選擇文件,點(diǎn)擊發(fā)送后可以看到服務(wù)器端返回的圖片鏈接。

第七步,進(jìn)入阿里云 OSS 后臺管理,可以確認(rèn)圖片確實(shí)已經(jīng)上傳成功。

三、拉取前端代碼來測試 OSS 上傳接口
codingmore-admin-web 是編程喵(Codingmore)的前端管理項(xiàng)目,可以通過下面的地址拉取到本地。
https://github.com/itwanger/codingmore-admin-web
執(zhí)行 yarn run dev 命令后就可以啟動 Web 管理端了,進(jìn)入到文章編輯頁面,選擇一張圖片進(jìn)行上傳,可以確認(rèn)圖片是可以正常從前端上傳到服務(wù)器端,服務(wù)器端再上傳到 OSS,之后再返回前端圖片訪問鏈接的。

四、利用 OSS 進(jìn)行自動轉(zhuǎn)鏈
第一步,在 PostsServiceImpl.java 中添加圖片轉(zhuǎn)鏈的方法,主要利用正則表達(dá)式找出文章內(nèi)容中的外鏈,然后將外鏈的圖片上傳到 OSS,然后再替換掉原來的外鏈圖片。
//?匹配圖片的?markdown?語法
//?
//?
public?static?final?String?IMG_PATTERN?=?"\\!\\[.*\\]\\((.*)\\)";
private?void?handleContentImg(Posts?posts)?{
????String?content?=?posts.getPostContent();
????Pattern?p?=?Pattern.compile(IMG_PATTERN,?Pattern.CASE_INSENSITIVE);
????Matcher?m?=?p.matcher(content);
????Map>?map?=?new?HashMap<>();
????while?(m.find())?{
????????String?imageTag?=?m.group();
????????LOGGER.info("使用分組進(jìn)行替換{}",?imageTag);
????????String?imageUrl?=?imageTag.substring(imageTag.indexOf("(")?+?1,?imageTag.indexOf(")"));
????????//?確認(rèn)是本站鏈接,不處理
????????if?(imageUrl.indexOf(iOssService.getEndPoint())?!=?-1)?{
????????????continue;
????????}
????????//?通過線程池將圖片上傳到?OSS
????????Future?future?=?ossUploadImageExecutor.submit(()?->?{
????????????return?iOssService.upload(imageUrl);
????????});
????????map.put(imageUrl,?future);
????}
????for?(String?oldUrl?:?map.keySet())?{
????????Future?future?=?map.get(oldUrl);
????????try?{
???????????String?imageUrl?=?future.get();
???????????content?=?content.replace(oldUrl,?imageUrl);
????????}?catch?(InterruptedException?|?ExecutionException?e)?{
????????????LOGGER.error("獲取圖片鏈接出錯{}",?e.getMessage());
????????}
????????
????}
????posts.setPostContent(content);
}?
第二步,在 OssServiceImpl.java 中添加根據(jù)外鏈地址上傳圖片到 OSS 的方法。
public?String?upload(String?url)?{
????String?objectName?=?getFileName(url);
????try?(InputStream?inputStream?=?new?URL(url).openStream())?{
????????ossClient.putObject(bucketName,?objectName,?inputStream);
????}?catch?(IOException?e)?{
????????LOGGER.error(e.getMessage());
????}
????return?formatOSSPath(objectName);
}
第三步,通過 Web 管理端來測試外鏈?zhǔn)欠褶D(zhuǎn)鏈成功。先找兩張外鏈的圖片,可以看到 markdown 在預(yù)覽的時(shí)候就不顯示。

然后我們點(diǎn)擊發(fā)布,可以看到兩張圖片都正常顯示了,因?yàn)檗D(zhuǎn)成了 OSS 的圖片訪問地址。

五、小結(jié)
綜上來看,實(shí)習(xí)生小二在 Spring Boot 中整合 OSS 的代碼還是挺靠譜的。也許 OSS+CDN 才是圖床的最好解決方案,不過阿里云的 HTTPS CDN 在 GitHub 上無法回源導(dǎo)致圖片不顯示的問題仍然沒有得到有效的解決。
需要源碼的小伙伴可以直接到編程喵??源碼路徑拉?。?/p>
https://github.com/itwanger/coding-more

沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟。
推薦閱讀:
