通过Google和Flickr图片源获取图片并以多线程方式保存图片

这是一个WebProject


首先是web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<servlet>
		<servlet-name>GetImageServlet</servlet-name>
		<servlet-class>com.jadyer.servlet.GetImageServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>SaveImageServlet</servlet-name>
		<servlet-class>com.jadyer.servlet.SaveImageServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>GetImageServlet</servlet-name>
		<url-pattern>/servlet/GetImageServlet</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>SaveImageServlet</servlet-name>
		<url-pattern>/servlet/SaveImageServlet</url-pattern>
	</servlet-mapping>
</web-app>

其次是index.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<script type="text/javascript" src="scripts/jquery-1.7.1.js"></script>
<script type="text/javascript">
	$(function(){
		$('#buttonQuery').click(function(){
			$.get('servlet/GetImageServlet',
				{
					categoryName: $('#categoryName').val(), //获取文本框中的值,即所要查询的关键字
					   picSource: $('input[name=picSource]:checked').val() //获取所选择的图片数据源的值			
				},
				function(returnedData, status){
					$('#picDiv').empty(); //清空这个<div>中的内容
					var html = '';
					for(var i=0; i<returnedData.length; i++){
						html += "<img src='" + returnedData[i] + "' style='width:200px; height:200px;'/>";
					}
					$('#picDiv').append(html);
				}
			);
		});
		
		$('#buttonSave').click(function(){
			$('#saveStatus').html("<font color='red'><b>Save Processing</b></font>");
			$.post('servlet/SaveImageServlet',
				function(result){
					$('#saveStatus').html("<font color='blue'><b>" + result + "</b></font>");
				}
			);
		});
	})
</script>

<input type="text" id="categoryName"/>
<input type="radio" name="picSource" value="google" checked="checked">Google
<input type="radio" name="picSource" value="flickr">Flickr
<input type="button" value="query" id="buttonQuery">
<input type="button" value="save" id="buttonSave">

<div id="saveStatus"></div>
<div id="picDiv"></div>

然后是专门处理网络连接的工具类NetworkUtil.java

package com.jadyer.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 专门处理网络连接的工具类
 * @author 宏宇
 * @editor Feb 9, 2012 11:51:14 AM
 */
public class NetworkUtil {
	/**
	 * 根据指定的URL获取其返回的内容
	 */
	public static String getStringContentFromURL(String url){
		StringBuffer sb = new StringBuffer();
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		try{
			is = new URL(url).openConnection().getInputStream();
			isr = new InputStreamReader(is, "UTF-8");
			br = new BufferedReader(isr);
			String line = null;
			while(null != (line=br.readLine())){
				sb.append(line);
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(null != br){
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(null != isr){
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(null != is){
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return sb.toString();
	}
	
	/**
	 * 完成文件的写入操作
	 */
	public static void writeToLocalFromInternet(String imageUrl, File file){
		InputStream is = null; //定义在外面,是因为我们要在finally{}中将其关掉
		OutputStream os = null;
		try{
			is = new URL(imageUrl).openStream();
			os = new FileOutputStream(file);
			int length = -1;
			byte[] buffer = new byte[7092];
			while(-1 != (length=is.read(buffer,0,7092))){
				os.write(buffer, 0, length);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(null != is){
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(null != os){
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

然后是处理图片保存操作的线程类SaveImageThread.java

package com.jadyer.util;

import java.io.File;

/**
 * 处理图片保存操作的线程类
 * @see 也可通过extends Thread类的方式,编写线程类,但人们更多的喜欢implements Runnable接口
 * @see 其实二者的效率,不见得就要差很多,可能implements Runnable更接近于面向对象的编程思想吧
 * @author 宏宇
 * @create Feb 11, 2012 12:38:08 AM
 */
public class SaveImageThread implements Runnable {
	private File directory;
	private String[] imageUrls;
	
	public SaveImageThread(File directory, String[] imageUrls){
		this.directory = directory;
		this.imageUrls = imageUrls;
	}

	public void run() {
		long startTime = System.currentTimeMillis();
		for(String imageUrl : imageUrls){
			int position = imageUrl.lastIndexOf("/");
			String imageName = imageUrl.substring(position + 1);
			File file = new File(directory, imageName);
			NetworkUtil.writeToLocalFromInternet(imageUrl, file);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("线程" + Thread.currentThread().getName() + "保存图片,使用时间:" + (endTime-startTime) + "ms");
	}
}

下面是用于获取图片的GetImageServlet.java

package com.jadyer.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.jadyer.util.NetworkUtil;

/**
 * 获取图片的Servlet
 * @author 宏宇
 * @editor Feb 9, 2012 2:27:58 AM
 */
public class GetImageServlet extends HttpServlet {
	private static final long serialVersionUID = -589346586269837420L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String categoryName = request.getParameter("categoryName");
		String picSource = request.getParameter("picSource");
		List<String> urlResult = new ArrayList<String>();

		String googleURL = "https://ajax.googleapis.com/ajax/services/search/images?rsz=large&v=1.0&"; 
		String flickrURL = "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=da8e8c6840579831969d1a87d37ae545&per_page=150&format=json&extras=original_format&tags=";
		if("google".equals(picSource)){
			urlResult = this.getGoogleImage(googleURL, categoryName);
		}else{
			urlResult = this.getFlickrImage(flickrURL, categoryName);
		}
		
		JSONArray jsonResult = JSONArray.fromObject(urlResult);
		response.setContentType("application/json; charset=UTF-8");
		response.setHeader("pragma", "no-cache");
		response.setHeader("cache-control", "no-cache");
		PrintWriter out = response.getWriter();
		out.print(jsonResult);
		out.flush();
		out.close();
		
		//将相关信息保存到session中,以便在保存图片时使用
		HttpSession session = request.getSession();
		session.setAttribute("categoryName", categoryName);
		session.setAttribute("picSource", picSource);
		session.setAttribute("images", urlResult);
	}
	
	/**
	 * Google图片或获取源
	 * @param googleURL    Google图片服务器的URL
	 * @param categoryName 所要查询的图片关键字
	 * @return 包含了所有查询出来的图片URL的一个List
	 */
	private List<String> getGoogleImage(String googleURL, String categoryName){
		//存储向客户端发送的url列表。对于Google来说,每次是64个URL。对于Flickr来说,可以设定每次返回多少个URL
		List<String> returnedList = new ArrayList<String>();
		//存储每次循环时,从Google所获取的json数据。对于Flickr来说则不需要这样,因为Flickr一次就能获得所有的图片
		List<String> jsonList = new ArrayList<String>();
		
		//每次取4*8张图片
		for(int i=0; i<4; i++){
			String url = new StringBuffer(googleURL).append("q=").append(categoryName).append("&start=").append(8*i).toString();
			jsonList.add(NetworkUtil.getStringContentFromURL(url));
		}
		
		for(String result : jsonList){
 			JSONObject jsonStr = JSONObject.fromObject(result);
			JSONObject responseData = jsonStr.getJSONObject("responseData");
			JSONArray results = responseData.getJSONArray("results");
			for(int i=0; i<results.size(); i++){
				JSONObject jsonObject = results.getJSONObject(i);
				returnedList.add(jsonObject.getString("url")); //得到图片的URL,并放到returnedList中
			}
		}
		return returnedList;
	}
	
	/**
	 * Flickr图片获取源
	 * @param flickrURL    Flickr图片服务器的URL
	 * @param categoryName 所要查询的图片关键字
	 * @return 包含了所有查询出来的图片URL的一个List
	 */
	private List<String> getFlickrImage(String flickrURL, String categoryName){
		List<String> returnedList = new ArrayList<String>();
		
		String jsonContent = NetworkUtil.getStringContentFromURL(flickrURL + categoryName);
		//将Flickr返回的json字符串中,开始部分的'jsonFlickrApi('和结尾部分的')'截掉,剩下的就是一个标准的json格式的字符串了
		jsonContent = jsonContent.substring(14, jsonContent.length()-1);
		
		JSONObject jsonObject = JSONObject.fromObject(jsonContent);
		//先获取到photos对应的对象,然后再获取到photo对应的数组
		JSONArray jsonArray = jsonObject.getJSONObject("photos").getJSONArray("photo");
		
		for(int i=0; i<jsonArray.size(); i++){
			JSONObject object = jsonArray.getJSONObject(i);
			String ID = object.getString("id");
			String secretID = object.getString("secret");
			String serverID = object.getString("server");
			int farmID = object.getInt("farm"); //Flickr返回的farm值是一个整型数字
			//拼接http://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}.jpg
			StringBuffer sb = new StringBuffer();
			sb.append("http://farm").append(farmID).append(".staticflickr.com/").append(serverID).append("/").append(ID).append("_").append(secretID).append(".jpg");
			returnedList.add(sb.toString());
		}
		return returnedList;
	}
}

最后是采用多线程方式保存图片的SaveImageServlet.java
package com.jadyer.servlet;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.jadyer.util.SaveImageThread;

/**
 * 采用多线程的方式保存图片的Servlet
 * @author 宏宇
 * @create Feb 10, 2012 1:33:37 AM
 */
public class SaveImageServlet extends HttpServlet {
	private static final long serialVersionUID = 5268619679635561228L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//得到在获取图片时,保存到session中的内容
		HttpSession session = request.getSession();
		String categoryName = (String)session.getAttribute("categoryName");
		String picSource = (String)session.getAttribute("picSource");
		List<String> list = (List<String>)session.getAttribute("images");
		
		//获取到WebRoot下面的storeImages文件夹的绝对路径,得到的这个path就表示storeImages文件夹在服务器端硬盘上的绝对路径
		String path = request.getSession().getServletContext().getRealPath("/storeImages");
		//根据查询的图片的关键字以及图片源构造子目录
		File directory = new File(path, picSource + "/" + categoryName);
		//将所有的目录,包括子目录,一并创建好
		directory.mkdirs();
		
//		/**
//		 * 单线程
//		 */
//		long startTime = System.currentTimeMillis();
//		for(String imageUrl : list){
//			//获取一个图片URL中的,图片名前面的"/"的位置
//			int postion = imageUrl.lastIndexOf("/");
//			//name of the image
//			String imageName = imageUrl.substring(postion+1);
//			//构造一个包含目录和图片名的File对象,接下来再以输出流的形式写到硬盘上
//			File file = new File(directory, imageName);
//			NetworkUtil.writeToLocalFromInternet(imageUrl, file);
//		}
//		System.out.println("单线程保存图片,合计使用时间:" + (System.currentTimeMillis()-startTime) + "ms");

//		/**
//		 * 多线程:每个线程处理1张图片
//		 */
//		String[] array2 = null;
//		for(int i=0; i<list.size(); i++){
//			array = new String[1];
//			String imageUrl = list.get(i);
//			array[0] = imageUrl;
//			new Thread(new SaveImageThread(directory, array)).start();
//		}
		
		/**
		 * 多线程:每个线程处理4张图片
		 */
		String[] array = null;
		for(int i=0; i<list.size(); i++){
			String imageUrl = list.get(i);
			//array[0]:0、4、8 、12
			//array[1]:1、5、9 、13
			//array[2]:2、6、10、14
			//array[3]:3、7、11、15
			//每个线程处理一个array数组中的四个元素
			if(i%4 == 0){
				if(0 != i){
					new Thread(new SaveImageThread(directory, array)).start();
				}
				array = new String[4];
			}
			array[i%4] = imageUrl;
		}
		
		response.setContentType("text/html");
		response.getWriter().print("Save Success");
		response.getWriter().flush();
		response.getWriter().close();
	}
}

补充:在GetImageServlet.java中,用到了Json-lib-2.4的相关jar。大家可以Email给我,我发给你们

           也可以直接在我的另一篇博文中找到下载方法,其访问地址为:使用Json-lib生成JSON文本

你可能感兴趣的:(多线程,String,function,Google,File,url)