1. 程式人生 > >java 使用圖片代理程式,解決網站圖片防盜鏈機制(測試百度,QQ空間有效)

java 使用圖片代理程式,解決網站圖片防盜鏈機制(測試百度,QQ空間有效)

業務場景

1、頁面引用其他站點圖片的時候,由於某些站點存在圖片的防盜鏈機制,所以在引用圖片的時候,返回的一張預設的圖片,而不是原圖片。
2、使用java完成一個代理程式,代理所有的存在防盜鏈機制的圖片請求,繞過防盜鏈機制,返回原圖片

解決思路

1、代理請求傳輸的http頭資訊Host,使用圖片url的Host,而自身站點的Host地址
2、代理請求傳輸的http頭資訊Referer,使用圖片url的Host,前面加上”http://”
3、完成以上1、2的操作,用以模擬圖片本身站點對圖片的請求,繞過反防盜鏈機制

代理相關的工具類

當中使用了Apache Commons IO

,解決的網路圖片獲取不完整的bug(需要的包請點選下載)

package lib;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import com.mysql.jdbc.StringUtils;
import dto.ImageData;

/**
 * 用於獲取url的圖片資料
 * @author yuyu
 *
 */
public class GetProxyImage { /** * 根據傳入的url將結果以byte[]的資料返回 * @param url 需要請求的url * @return * @throws Exception */ public static ImageData getImage(String url)throws Exception{ //當傳入的url返回不為空的時候,讀取資料 ImageData back=null; InputStream input; if
(!StringUtils.isNullOrEmpty(url)){ try{ back =new ImageData(); String ContentType=null; //設定請求的頭資訊 URL urlInfo = new URL(url); URLConnection connection = urlInfo.openConnection(); //設定頭資訊 connection.addRequestProperty("Host", urlInfo.getHost()); connection.addRequestProperty("Connection", "keep-alive"); //強制要求快取伺服器在返回快取的版本之前將請求提交到源頭伺服器進行驗證。 connection.addRequestProperty("Cache-Control", "no-cache"); //使用url的Host來標記來源 connection.addRequestProperty("Referer", "http://"+urlInfo.getHost()); //表示使用者不願意目標站點追蹤使用者個人資訊。 connection.addRequestProperty("DNT", "1"); //強制要求快取伺服器在返回快取的版本之前將請求提交到源頭伺服器進行驗證。 connection.addRequestProperty("Pragma", "no-cache"); connection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"); connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36"); connection.connect(); //獲取頭資訊,圖片的返回格式 Map<String, List<String>> map = connection.getHeaderFields(); List<String> type=map.get("Content-Type"); ContentType=type.get(0); //獲取請求回來的資訊 input = connection.getInputStream(); //解決使用inputStream.available(),讀取圖片不完整問題 //此方法在內部緩衝輸入,因此不需要使用BufferedInputStream。 byte[]data = IOUtils.toByteArray(input); input.close(); //設定返回資訊 back.setContentType(ContentType); back.setData(data); }catch(Exception e){ throw new Exception("讀取Url資料失敗:"+url,e); } } return back; } }

用以傳輸圖片物件的簡單類

package dto;

/**
 * 用於http圖片資料請求
 * @author yuyu
 *
 */
public class ImageData {

    //圖片返回的格式資訊
    private String ContentType;
    //圖片返回資料的位元組陣列
    private byte[] data;

    public String getContentType() {
        return ContentType;
    }
    public void setContentType(String contentType) {
        ContentType = contentType;
    }
    public byte[] getData() {
        return data;
    }
    public void setData(byte[] data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return "ImageData [ContentType=" + ContentType + ", data長度="
                + data.length + "]";
    }

}

處理代理請求的Servlet

package proxyImage;

import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import dto.ImageData;
import lib.GetProxyImage;

@SuppressWarnings("serial")
public class ProxyImage extends HttpServlet {

    /**
     * 接收一個代理圖片的url,返回代理得來的圖片資料
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String url=request.getParameter("url");

        //獲取圖片資訊
        try {
            ImageData image = GetProxyImage.getImage(url);

            System.out.println(image);

            if(image!=null){
                //將獲取到的圖片返回
                response.setContentType(image.getContentType());
                //設定瀏覽器快取
                response.setHeader("Cache-Control", "max-age=31536000");
                OutputStream out = response.getOutputStream();
                out.write(image.getData());         
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

展示jsp頁面

頁面的具體資料繫結使用了vue 框架(詳情請點選檢視)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>   
    <title>proxy image page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>

  </head>

  <body style="text-align:center">
    <div id="app" >
        <h2>{{ message }}</h2>  
        <input v-model="imageUrl" style="width: 800px;height: 30px;" Placeholder="請輸入圖片地址">
        <br>
        <div  style="display: inline-block;">   
            <h5>原圖片</h5>
            <img :src="imageUrl" style="width: 400px;height: 300px;">
        </div>
        <div style="display: inline-block;">
            <h5>代理圖片</h5>
            <img :src="proxyImageUrl" style="width: 400px;height: 300px;">
        </div>
    </div>
    <script>
        var app = new Vue({
              el: '#app',
              data: {
                message: '圖片代理展示',
                imageUrl: ''
              },
              computed: {
                  proxyImageUrl: function () {
                      return  'servlet/ProxyImage?url='+encodeURIComponent(this.imageUrl)
                  }
              }
            })
    </script>
  </body>
</html>

測試

總結

1、如果代理的圖片過大,或者過於頻繁其實可以將代理的結果快取起來
2、這個代理程式只適合於其他站點的靜態圖片獲取,要是動態的隱私圖片可能會根據登入使用者的cookie或者ip顯示,這個時候就會代理失敗