在搭建自己的博客網站的時候,很有可能要引入一些外部圖片,畢竟多數人最開始不是在自己的平台上寫博客。
因某種需要,搬運自己以前寫的博客到自己的網站時,在圖片這一步可能會出現問題,無法顯示。其中往往就是防盜鏈在起作用了
防盜鏈
定義
百度百科給的解釋是
此內容不在自己服務器上,而通過技術手段,繞過別人放廣告有利益的最終頁,直接在自己的有廣告有利益的頁面上向最終用戶提供此內容。 常常是一些名不見經傳的小網站來盜取一些有實力的大網站的地址(比如一些音樂、圖片、軟件的下載地址)然后放置在自己的網站中,通過這種方法盜取大網站的空間和流量。
可能有點不易懂,通俗的講,就是 出於某些原因(服務器或圖片來源等),從別的網站引入圖片到自己的網站中。這樣連服務器的存儲空間費用都省了( •̀ ω •́ )✧
原理
防盜鏈實現的原理就是:http協議中有個叫referer的表頭字段,采用URL的格式來表示從哪兒鏈接到當前的網頁或文件。換句話說,通過referer,網站可以檢測目標網頁訪問的來源網頁,如果是資源文件,則可以跟蹤到顯示它的網頁地址。有了referer跟蹤來源,就可以通過技術手段來進行處理,一旦檢測到來源不是本站即進行阻止或者返回指定的頁面。比如防盜鏈通常返回的403 forbidden。
實現
apache 服務器通常使用它的 Url Rewrite功能來實現
具體可參考 這篇文章
反防盜鏈
任何事物都有相對的兩面,既然有了防盜鏈,那么肯定也有反防盜鏈。
下載文件
將目標文件下載到自己的服務器,然后從本地讀取,這個方法應該算是比較憨厚的一種了。不過准確說這個也不算啥反防盜鏈
使用代理模式
就是下面詳細講的代理模式
文件代理模式
由於防盜鏈的關鍵在於referer頭部, 而瀏覽器獲取圖片時,它會老實的填上真是來源。比如從某個博客訪問,就填上博客地址。不過我們手動訪問圖片地址時,是不帶referer的,這時就可以訪問。因此,我們可以實現一個代理模式,來轉發圖片。
比如有防盜鏈的圖片地址 www.aa.com, 服務器地址 www.bb.com, 先讓服務器來請求(不帶referer)得到圖片,再將請求到的圖片轉發給目標網站(比如個人博客網站)。也就是博客上的圖片src直接填
www.bb.com/forward?url=www.aa.com
當然我們要在服務器上實現相應的圖片轉發功能。
下面將以springboot 為例,實現圖片轉發:
首先設置相應接口
@RequestMapping(value = "/forward")
public ResponseEntity<Resource> forward(@RequestParam(name = "url", required = true) String url){
System.out.println("Start: ------------------------->");
Resource resource = null;
InputStream inputStream;
try {
inputStream = new URL(url).openStream();
resource = new ByteArrayResource(toByteArray(inputStream));
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.header(HttpHeaders.CONTENT_DISPOSITION, " filename=\"" + getFileName(url) + "\"")
.body(resource);
}
加上ResponseEntity是為了更好的控制Response,比如響應的頭部信息等。
其中的toByteArray()函數為
private byte[] toByteArray(InputStream in){
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
byte[] bytes = new byte[1024];
int n;
while ((n = in.read(bytes)) != -1){
byteArrayOutputStream.write(bytes, 0, n);
}
System.out.println(byteArrayOutputStream.size());
} catch (Exception e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
getFileName()函數為
private String getFileName(String url){
String[] strs = url.split("/");
for (String str: strs){
// 這里我僅處理png格式的,根據需求修改即可
if (str.toLowerCase().endsWith(".png")){
return str;
}
}
return null;
}
最后
就以cnblog上一圖片 https://img2018.cnblogs.com/blog/1470456/201812/1470456-20181206174653015-309070173.png 舉個例子
如圖,轉發成功(~ ̄▽ ̄)~

