微博圖床掛了!

一直擔(dān)心的事情還是發(fā)生了。
作為hexo多年的使用者,微博圖床一直是我的默認(rèn)選項(xiàng),hexo+typora+iPic更是我這幾年寫文章的黃金組合。而圖床中,新浪圖床一直都是我的默認(rèn)選項(xiàng),速度快、穩(wěn)定同時(shí)支持大圖片批量上傳更是讓其成為了眾多圖床工具的默認(rèn)選項(xiàng)。雖然今年早些的時(shí)候,部分如「ws1、ws2……」的域名就已經(jīng)無法使用了,但通過某些手段還是可以讓其存活的,而最近,所有調(diào)用的微博圖床圖片都無法加載并提示“403 Forbidden”了。
如果是Mac用戶,又想偷懶,我這推薦ipic Move遷移工具,可以把微博的圖床自動(dòng)下載下來,轉(zhuǎn)成ipic域名的圖床。


??Tips:圖片中出現(xiàn)的Tengine是淘寶在Nginx的基礎(chǔ)上修改后開源的一款Web服務(wù)器,基本上,Tengine可以被看作一個(gè)更好的Nginx,或者是Nginx的超集,詳情可參考??淘寶Web服務(wù)器Tengine正式開源 - The Tengine Web Server
剛得知這個(gè)消息的時(shí)候,我的第一想法其實(shí)是非常生氣的,畢竟自己這幾年上千張圖片都是用的微博圖床,如今還沒備份就被403了,可仔細(xì)一想,說到底還是把東西交在別人手里的下場,微博又不是慈善企業(yè),也要控制成本,一直睜一只眼閉一只眼讓大家免費(fèi)用就算了,出了問題還是不太好怪到微博上來的。
那么有什么比較好的辦法解決這個(gè)問題呢?
查遍了網(wǎng)上一堆復(fù)制/粘貼出來的文章,不是開啟反向代理就是更改請求頭,真正愿意從根本上解決問題的沒幾個(gè)。
如果不想將自己沉淀的博客、文章托管在印象筆記、notion、語雀這些在線平臺(tái)的話,想要徹底解決這個(gè)問題最好的方式是:自建圖床!
為了更好的解決問題,我們先弄明白,403是什么,以及我們存在微博上的圖片究竟是如何被403的。
403
百度百科,對于403錯(cuò)誤的解釋很簡單
403錯(cuò)誤是一種在網(wǎng)站訪問過程中,常見的錯(cuò)誤提示,表示資源不可用。服務(wù)器理解客戶的請求,但拒絕處理它,通常由于服務(wù)器上文件或目錄的權(quán)限設(shè)置導(dǎo)致的WEB訪問錯(cuò)誤。
所以說到底是因?yàn)樵L問者無權(quán)訪問服務(wù)器端所提供的資源。而微博圖床出現(xiàn)403的原因主要在于微博開啟了防盜鏈。
防盜鏈的原理很簡單,站點(diǎn)在得知有請求時(shí),會(huì)先判斷請求頭中的信息,如果請求頭中有Referer信息,然后根據(jù)自己的規(guī)則來判斷Referer頭信息是否符合要求,Referer 信息是請求該圖片的來源地址。
如果盜用網(wǎng)站是?https?的 協(xié)議,而圖片鏈接是 http 的話,則從 https 向 http 發(fā)起的請求會(huì)因?yàn)榘踩缘囊?guī)定,而不帶 referer,從而實(shí)現(xiàn)防盜鏈的繞過。官方輸出圖片的時(shí)候,判斷了來源(Referer),就是從哪個(gè)網(wǎng)站訪問這個(gè)圖片,如果是你的網(wǎng)站去加載這個(gè)圖片,那么 Referer 就是你的網(wǎng)站地址;你的網(wǎng)址肯定沒在官方的白名單內(nèi),(當(dāng)然作為可操作性極強(qiáng)的瀏覽器來說 referer 是完全可以偽造一個(gè)官方的 URL 這樣也也就也可以饒過限制??)所以就看不到圖片了。

解決問題
解釋完原理之后我們發(fā)現(xiàn),其實(shí)只要想辦法在自己的個(gè)人站點(diǎn)中設(shè)置好referer就可以解決這個(gè)問題,但說到底也只是治標(biāo)不治本,真正解決這個(gè)問題就是想辦法將圖片遷移到自己的個(gè)人圖床上。
現(xiàn)在的圖床工具很多,iPic、uPic、PicGo等一堆工具既免費(fèi)又開源,問題在于選擇什么云存儲(chǔ)服務(wù)作為自己的圖床以及如何替換自己這上千張圖片。
- 選擇什么云存儲(chǔ)服務(wù)
- 如何替換上千張圖片
什么是OSS以及如何選擇
「OSS」的英文全稱是Object Storage Service,翻譯成中文就是「對象存儲(chǔ)服務(wù)」,官方一點(diǎn)解釋就是對象存儲(chǔ)是一種使用HTTP API存儲(chǔ)和檢索非結(jié)構(gòu)化數(shù)據(jù)和元數(shù)據(jù)對象的工具。
白話文解釋就是將系統(tǒng)所要用的文件上傳到云硬盤上,該云硬盤提供了文件下載、上傳等一列服務(wù),這樣的服務(wù)以及技術(shù)可以統(tǒng)稱為OSS,業(yè)內(nèi)提供OSS服務(wù)的廠商很多,知名常用且成規(guī)模的有阿里云、騰訊云、百度云、七牛云、又拍云等。
對于我們這些個(gè)人用戶來說,這些云廠商提供的服務(wù)都是足夠使用的,我們所要關(guān)心的便是成本??。
筆者使用的是七牛云,它提供了10G的免費(fèi)存儲(chǔ),基本已經(jīng)夠用了。
有人會(huì)考慮將GitHub/Gitee作為圖床,并且這樣的文章在中文互聯(lián)網(wǎng)里廣泛流傳,因?yàn)楹芏嗳说膫€(gè)人站點(diǎn)都是托管在GitHub Pages上的,但是個(gè)人建議是不要這么做。
首先GitHub在國內(nèi)的訪問就很受限,很多場景都需要科學(xué)上網(wǎng)才能獲得完整的瀏覽體驗(yàn)。再加上GitHub官方也不推薦將Git倉庫存儲(chǔ)大文件,GitHub建議倉庫保持較小,理想情況下小于 1 GB,強(qiáng)烈建議小于 5 GB。
如何替換上千張圖片
替換文章中的圖片鏈接和“把大象放進(jìn)冰箱里”步驟是差不多的
- 下載所有的微博圖床的圖片
- 上傳所有的圖片到自己的圖床(xx云)
- 對文本文件執(zhí)行replaceAll操作
考慮到我們需要遷移的文件數(shù)量較多,手動(dòng)操作肯定是不太可行的,因此我們可以采用代碼的方式寫一個(gè)腳本完成上述操作??紤]到自己已經(jīng)是一個(gè)成熟的Java工程師了,這個(gè)功能就干脆用Java寫了。
為了減少代碼量,精簡代碼結(jié)構(gòu),我這里引入了幾個(gè)第三方庫,當(dāng)然不引入也行,如果不引入有一些繁瑣而又簡單的業(yè)務(wù)邏輯需要自己實(shí)現(xiàn),有點(diǎn)浪費(fèi)時(shí)間了。
整個(gè)腳本邏輯非常簡單,流程如下:

獲取博客文件夾下的Markdown文件
這里我們直接使用hutool這個(gè)三方庫,它內(nèi)置了很多非常實(shí)用的工具類,獲取所有markdown文件也變得非常容易
/**
?*?篩選出所有的markdown文件
?*/
public?static?List<File>?listAllMDFile()?{
????List<File>?files?=?FileUtil.loopFiles(VAULT_PATH);
????return?files.stream()
???????.filter(Objects::nonNull)
????????.filter(File::isFile)
????????.filter(file?->?StringUtils.endsWith(file.getName(),?".md"))
????????.collect(Collectors.toList());
}
獲取文件中的所有包含微博圖床的域名
通過Hutools內(nèi)置的FileReader我們可以直接讀取markdown文件的內(nèi)容,因此我們只需要解析出文章里包含微博圖床的鏈接即可。我們可以借助正則表達(dá)式快速獲取一段文本內(nèi)容里的所有url,然后做一下filter即可。
/**
?*?獲取一段文本內(nèi)容里的所有url
?*
?*?@param?content?文本內(nèi)容
?*?@return?所有的url
?*/
public?static?List<String>?getAllUrlsFromContent(String?content)?{
????List<String>?urls?=?new?ArrayList<>();
????Pattern?pattern?=?Pattern.compile(
????????"\\b(((ht|f)tp(s?)\\:\\/\\/|~\\/|\\/)|www.)"?+?"(\\w+:\\w+@)?(([-\\w]+\\.)+(com|org|net|gov"
????????????+?"|mil|biz|info|mobi|name|aero|jobs|museum"?+?"|travel|[a-z]{2}))(:[\\d]{1,5})?"
????????????+?"(((\\/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?"?+?"((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?"
????????????+?"([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)"?+?"(&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?"
????????????+?"([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*"?+?"(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?\\b");
????Matcher?matcher?=?pattern.matcher(content);
????while?(matcher.find())?{
????????urls.add(matcher.group());
????}
????return?urls;
}
下載圖片
用Java下載文件的代碼在互聯(lián)網(wǎng)上屬實(shí)是重復(fù)率最高的一批檢索內(nèi)容了,這里就直接貼出代碼了。
public?static?void?download(String?urlString,?String?fileName)?throws?IOException?{
????File?file?=?new?File(fileName);
????if?(file.exists())?{
????????return;
????}
????URL?url?=?null;
????OutputStream?os?=?null;
????InputStream?is?=?null;
????try?{
????????url?=?new?URL(urlString);
????????URLConnection?con?=?url.openConnection();
????????//?輸入流
????????is?=?con.getInputStream();
????????//?1K的數(shù)據(jù)緩沖
????????byte[]?bs?=?new?byte[1024];
????????//?讀取到的數(shù)據(jù)長度
????????int?len;
????????//?輸出的文件流
????????os?=?Files.newOutputStream(Paths.get(fileName));
????????//?開始讀取
????????while?((len?=?is.read(bs))?!=?-1)?{
????????????os.write(bs,?0,?len);
????????}
????}?finally?{
????????if?(os?!=?null)?{
????????????os.close();
????????}
????????if?(is?!=?null)?{
????????????is.close();
????????}
????}
}
上傳圖片
下載完圖片后我們便要著手將下載下來的圖片上傳至我們自己的云存儲(chǔ)服務(wù)了,這里直接給出七牛云上傳圖片的文檔鏈接了,文檔里寫的非常詳細(xì),我就不贅述了??
Java SDK_SDK 下載_對象存儲(chǔ) - 七牛開發(fā)者中心
全局處理
通過閱讀代碼的細(xì)節(jié),我們可以發(fā)現(xiàn),我們的方法粒度是單文件的,但事實(shí)上,我們可以先將所有的文件遍歷一遍,統(tǒng)一進(jìn)行圖片的下載、上傳與替換,這樣可以節(jié)約點(diǎn)時(shí)間。
統(tǒng)一替換的邏輯也很簡單,我們申明一個(gè)全局Map,
private?static?final?Map<String,?String>?URL_MAP?=?Maps.newHashMap();
其中,key是舊的新浪圖床的鏈接,value是新的自定義圖床的鏈接。
我們將listAllMDFile這一步中所獲取到的所有文件里的所有鏈接保存于此,下載時(shí)只需遍歷這個(gè)Map的key即可獲取到需要下載的圖片鏈接。然后將上傳后得到的新鏈接作為value存在到該Map中即可。
全文替換鏈接并更新文件
有了上述這些處理步驟,接下來一步就變的異常簡單,只需要遍歷每個(gè)文件,將匹配到全局Map中key的鏈接替換成Map中的value即可。
/**
?*?替換所有的圖片鏈接
?*/
private?static?String?replaceUrl(String?content,?Map<String,?String>?urlMap)?{
????for?(Map.Entry<String,?String>?entry?:?urlMap.entrySet())?{
????????String?oldUrl?=?entry.getKey();
????????String?newUrl?=?entry.getValue();
????????if?(StringUtils.isBlank(newUrl))?{
????????????continue;
????????}
????content?=?RegExUtils.replaceAll(content,?oldUrl,?newUrl);
????}
????return?content;
}
我們借助commons-lang實(shí)現(xiàn)字符串匹配替換,借助Hutools實(shí)現(xiàn)文件的讀取和寫入。
files.forEach(file?->?{
????try?{
????????FileReader?fileReader?=?new?FileReader(file.getPath());
????????String?content?=?fileReader.readString();
????????String?replaceContent?=?replaceUrl(content,?URL_MAP);
????????FileWriter?writer?=?new?FileWriter(file.getPath());
????????writer.write(replaceContent);
????}?catch?(Throwable?e)?{
????????log.error("write?file?error,?errorMsg:{}",?e.getMessage());
????}
});
為了安全起見,最好把文件放在新的目錄中,不要直接替換掉原來的文件,否則程序出現(xiàn)意外就麻煩了。
接下來我們只需要運(yùn)行程序,靜待備份結(jié)果跑完即可。
推薦項(xiàng)目
如果想學(xué)Java項(xiàng)目的,我還是
強(qiáng)烈推薦
我的開源項(xiàng)目消息推送平臺(tái)Austin,可以用作
畢業(yè)設(shè)計(jì)
,可以用作
校招
,可以看看
生產(chǎn)環(huán)境是怎么推送消息
的。
倉庫地址(可點(diǎn)擊閱讀原文跳轉(zhuǎn)):https://gitee.com/zhongfucheng/austin
我開通了 股東服務(wù) 內(nèi)容,感興趣可以點(diǎn)擊下方看看,主要針對的是項(xiàng)目喲
