1.这样获取网络图片对应的文件流
要下载一张网络图片,比如百度的Logo图片,地址是:http://www.baidu.com/img/bdlogo.gif
当然也可能是你所在的局域网, 比如把OA内部办公系统的新闻图片下载到本机。这种情况下你就得
确定是否要通过代理才能访问外部网络了。但是不管哪种情况,下载图片其实很平常,就是为了获取InputStream流
然后读取流里面的内容,保存到本机或者图片服务器。怎么样才能获得这个流呢,可以想下平常我们上传图片是怎么实现的。
无非就是Form表单来一次POST提交,在后台你用Servlet就能获取到上载的文件流了。
因此你知道一张图片地址,就可以针对这个地址模拟一次表单请求,就能获取到这个图片资源对应的流了。
2.下载网络图片代码示例
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
/**
*
* 〈一个简单的下载网络图片工具类,可以下载内网或者代理下载外网图片〉
*
* @author yli
*/
public class HttpProxyUtil {
public static void main(String[] args) throws MalformedURLException, IOException {
// 下载图片,不经过代理
// String url1 = "http://oa.xxx.com/infoResource/imgs/1.jpg";
// String saveUrl1 = "src/io/oa.jpg";
// downImgFromInetDemo(url1, saveUrl1, null);
// 经过代理下载图片
String host = "192.168.120.177";
int port = 34878;
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
String url2 = "http://www.baidu.com/img/bdlogo.gif";
String saveUrl2 = "src/io/bdlogo.gif";
downImgFromInetDemo(url2, saveUrl2, proxy);
}
/**
*
* 〈下载网络图片方法示例〉
*
* @param targetUrl[要下载的图片对应的网络地址]
* @param saveUrl[要保存的地址]
* @param proxy [不通过代理下载就直接传递null进来]
* @throws MalformedURLException
* @throws IOException
*/
public static void downImgFromInetDemo(String targetUrl, String saveUrl, Proxy proxy)
throws MalformedURLException, IOException {
// 读取InputStream字节流,将图片内容读取到一个字节数组
// 该字节数组就可以重复利用
byte[] imgBytes = getByteArrayFromInputStream(proxy, targetUrl);
// 获取图片基本信息:大小、宽度、高度和格式
ImageInfo targetImg = getImageInfoFromInputStream(imgBytes);
targetImg.setSize(imgBytes.length);
// 实例化一个符合业务要求的图片对象
ImageInfo validImg = new ImageInfo();
validImg.setSize(500 * 1024); // 最大允许500KB
validImg.setWidth(1500); // 最宽允许 1500px
validImg.setHeight(1200); // 最高允许1200px
validImg.setValidTypes("jpg", "jpeg", "gif", "png", "bmp");
// 校验图片是否符合业务要求
if (targetImg.isValidImg(validImg)) {
// 保存图片
ByteArrayInputStream in = new ByteArrayInputStream(imgBytes);
saveImgFromInputStream(in, saveUrl);
System.out.println("===>图片符合要求,保存成功");
} else {
System.out.println("===>图片不符合要求!");
}
// 可以简单的打印图片基本信息
System.out.println(targetImg);
System.out.println(validImg);
}
/**
*
* 〈将图片文件流转换成字节数组〉
* 〈好处就是字节数组可以多次利用,而流一旦读取过一次之后就不能再使用了〉
*
* @param proxy
* @param targetUrl
* @return
* @throws MalformedURLException
* @throws IOException
*/
private static byte[] getByteArrayFromInputStream(Proxy proxy, String targetUrl)
throws MalformedURLException, IOException {
HttpURLConnection urlConnection;
if (null == proxy) {
urlConnection = (HttpURLConnection) new URL(targetUrl).openConnection();
} else {
// 如果有代理则通过代理下载
urlConnection = (HttpURLConnection) new URL(targetUrl).openConnection(proxy);
}
// 把文件写到字节数组保存起来
BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
ByteArrayOutputStream fos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
byte[] bytes = fos.toByteArray();
bis.close();
fos.close();
urlConnection.disconnect();
return bytes;
}
/**
*
* 〈将流保存至本机或者图片服务器〉
*
* @param in
* @param saveUrl
* @throws IOException
*/
public static void saveImgFromInputStream(InputStream in, String saveUrl) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(saveUrl));
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
in.close();
}
/**
*
* 〈如果你已经将图片文件流InputStream读取出来放到一个字节数组〉
* 〈那么根据这个字节数组也是可以转换成对应的图片流,并再次获取图片基本信息〉
*
* @param imgBytes
* @return
*/
public static ImageInfo getImageInfoFromInputStream(byte[] imgBytes) {
ByteArrayInputStream in = new ByteArrayInputStream(imgBytes);
ImageInfo image = getImageInfoFromInputStream(in);
return image;
}
/**
*
* 〈从图片文件流读取图片文件的基本信息〉
*
* @param inputStream
* @return
*/
public static ImageInfo getImageInfoFromInputStream(InputStream inputStream) {
ImageInputStream imgStream = null;
try {
// 创建Image流
imgStream = ImageIO.createImageInputStream(inputStream);
Iterator<ImageReader> iter = ImageIO.getImageReaders(imgStream);
if (!iter.hasNext()) {
return null;
}
// 读取流中一帧就可以获取到高宽以及各式
ImageReader reader = iter.next();
reader.setInput(imgStream, true);
int width = reader.getWidth(0);
int height = reader.getHeight(0);
String type = reader.getFormatName();
ImageInfo bean = new ImageInfo();
bean.setWidth(width);
bean.setHeight(height);
bean.setType(type);
return bean;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
imgStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* 〈一般下载图片都会有一些简单的业务检验,比如大小格式等〉
* 〈因此定义一个图片类,来描述图片的基本属性〉
*
* @author yli
*/
class ImageInfo {
// 图片大小
private long size = 0;
// 图片宽度
private int width = 0;
// 图片高度
private int height = 0;
// 图片类型
private String type = "jpg";
// 符号要求的图片类型
private String[] validTypes;
/**
*
* 〈定义一个简单方法描述该图片是否符合要求〉
*
* @param validImg
* @return
*/
public boolean isValidImg(ImageInfo validImg) {
if (null == validImg) {
return true;
}
return (this.getSize() <= validImg.getSize()) && (this.getWidth() <= validImg.getWidth())
&& (this.getHeight() <= validImg.getHeight()) && isValidType(validImg);
}
private boolean isValidType(ImageInfo validImg) {
if (null == validImg.getValidTypes()) {
return true;
}
boolean isValid = false;
for (String validType : validImg.getValidTypes()) {
if (type.equalsIgnoreCase(validType)) {
isValid = true;
break;
}
}
return isValid;
}
@Override
public String toString() {
return String.format("{size:%s,width:%s,height:%s,type:%s,validType:%s}", size, width,
height, type, validTypeToString());
}
private String validTypeToString() {
if (null == validTypes) {
return "";
}
StringBuffer sb = new StringBuffer("[");
for (String type : validTypes) {
sb.append(type).append(" ");
}
sb.append("]");
return sb.toString();
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String[] getValidTypes() {
return validTypes;
}
public void setValidTypes(String... types) {
this.validTypes = types;
}
}
3.通过代理下载网络图片的一些常见问题
(1)打开HttpURLConnection报401错误,一般来说说你发起的请求方式被拒绝
比如你发起的是 POST 请求,可能对方只能接受 GET 请求,通过这种设置你可以试试
urlConnection.setRequestMethod("GET"); 或者 urlConnection.setRequestMethod("POST");
(2)打开HttpURLConnection报503错误,可能是你用Java模拟的表单请求,被对方拒绝
那你可以尝试就像浏览器发起请求一样,来欺骗下对方,这样设置试试
urlConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
(3)打开HttpURLConnection报403错误,可能是你仅仅只设置了代理的地址和端口,没设置密码,可以这样试试
String headerKey = "Proxy-Authorization";
String headerValue = "Basic " + new BASE64Encoder().encode("你的用户名:你的密码".getBytes());
urlConnection.setRequestProperty(headerKey, headerValue);
这样还不行的话,再试试另外一种方式,先定义一个密码类
public class BasicAuthenticator extends Authenticator {
String userName;
String password;
public BasicAuthenticator(String userName, String password) {
this.userName = userName;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password.toCharArray());
}
}
然后再创建完urlConnection之后,设置一下访问的用户名和密码
urlConnection = (HttpURLConnection) new URL(url).openConnection(proxy);
Authenticator.setDefault(new BasicAuthenticator(userName, password));