您的位置:首页 > 运维架构 > 网站架构

java 使用图片代理程序,解决网站图片防盗链机制(测试百度,QQ空间有效)

2018-03-22 15:55 736 查看

业务场景

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.getIm
e82c
age(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、从百度引用的图片地址:

http://a.hiphotos.baidu.com/image/w%3D230/sign=fa52b971b1de9c82a665fe8c5c8180d2/d53f8794a4c27d1eba11668c19d5ad6eddc43846.jpg

2、代理截图:



二、QQ空间

1、从QQ空间引用的图片地址:

http://a4.qpic.cn/psb?/f2006c66-5773-4a59-8984-2cbea031e9c0/WB*zO7jU3cQhrGgLSKf9Vd5c.YNcq67BGOWQmdO1qkk!/m/dEMBAAAAAAAA

2、代理截图:



总结

1、如果代理的图片过大,或者过于频繁其实可以将代理的结果缓存起来

2、这个代理程序只适合于其他站点的静态图片获取,要是动态的隐私图片可能会根据登入用户的cookie或者ip显示,这个时候就会代理失败
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java http