HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.2 (GA)。系统中用来实现信息爬取.
笔者寄语
腾讯空间备份器,个人玩乐之作,站在前人的肩膀上, 对知识的巩固,也顺便练练手,熟悉一下Swing编程,好久都不用了,项目虽小,五脏俱全,与大家交流分享.
项目功能:对指定QQ用户,执行空间相册批量备份,日志批量备份、留言备份、说说备份.
技术说明:使用HttpClient和_HtmlParser实现简易爬虫,信息提取
MVC思想在Swing应用程序中的使用,Log4j进行系统日志的记录.
用户信息通过配置文件获取.
本文旨在谈如何在Swing应用程序中使用MVC思想,所以代码只贴核心模块.
这里谈谈个人对MVC的理解,为初学者做个简单的介绍.
MVC是一种经典的程序设计理念.
此模式将程序分为3个部分,分别为模型层(Model)、视图层(View)、控制层(Controller).
模型层(Model)
模型层是应用程序的核心部分,主要由JavaBean组件来充当,可以是一个实体对象或一种业务逻辑,之所以称之为模型,是因为它在应用程序中有更好的重用性、扩展性.
视图层(View)
视图层提供应用程序与用户之间的交互界面,在MVC模式中,这一层并不包含任何的业务逻辑,仅仅提供一种与用户相交互的视图.在Web应用中由JSP、HTML界面充当.
控制层(Controller)
控制层用于对程序中的请求进行控制,起到一种宏观调控的作用,它可以通过容器选择什么样的视图、什么样的模型组件、在Web应用中由Servlet充当.
项目界面截图
用到的知识点就这么点,接下来咱代码说话,。
项目架构如下
从项目架构就可以很明显的看出MVC思想如何应用的了,接下来,分析一下我的模块设计
log4j.properties文件不用说了,当然是为配置日志输出的,关于Log4j的使用与总结,参照http://www.cnblogs.com/dennisit/archive/2013/01/01/2841603.html
系统中的QQ用户是通过userlist_config.xml来初始化系统数据的.因为是练手,所以就多的绕了这么一圈,当然你也可以直接设计成用户输入.
说明一下我的XML文档设计
<?xml version="1.0" encoding="UTF-8"?>
<userList>
<!-- 标准属性配置样式
name : 用户的QQ,配置文件中一般不允许重复,如果配置重复则以第一个为准
-->
<user name="799089378" >
<!-- name: 用户模块(值=为日志、相册、说说、留言),
tag: 界面显示的标签
savePath: 图片本地保存路径
-->
<type name="日志" tag="苏若年吧" savePath="D:/PictureCrawler/799089378/diary/"></type>
<type name="相册" tag="我的相册" savePath="D:/PictureCrawler/799089378/album/"></type>
<type name="说说" tag="快乐心情" savePath="D:/PictureCrawler/799089378/state/"></type>
<type name="留言" tag="留言模块" savePath="D:/PictureCrawler/799089378/news/"></type>
</user>
<user name="2601184786" >
<type name="日志" tag="老大扯淡" savePath="D:/PictureCrawler/2601184786/album/"></type>
<type name="相册" tag="老大艳照" savePath="D:/PictureCrawler/2601184786/album/"></type>
</user>
<user name="1377295526" >
<type name="说说" tag="阿中姑娘的说说" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="留言" tag="阿中姑娘的留言" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="日志" tag="阿中姑娘的日志" savePath="D:/PictureCrawler/1377295526/album/"></type>
<type name="相册" tag="阿中姑娘的相册" savePath="D:/PictureCrawler/1377295526/album/"></type>
</user>
</userList>
每个标签及属性的功能都在注释中说明,就不多说了,
配置的部分就这么搞定了
接下来我们以相册模块来分析,模块是相同的,一个模块的过程顺了,其它模块举一反三自然也就会了,项目中复杂的是业务逻辑.
接下来看Model怎么应用,Model即我们Java开发中的Bean,分为数据Bean和业务Bean.
这里的实体Bean.即为系统所用的实体对象.本项目中在org.crawler.picture.dennisit.entity包下.
包下的实体有Album(对应QQ用户的相册)、Photo(对应每一张照片)、Type(对应配置文件的type表现)、User(对应配置文件的每一个用户标签)
Album(id<相册ID>, name<相册名称>,imgcount<相册图片数目>)
Photo(Album<所属相册>,url<照片路径>,name<照片名称>)
Type(name<类别名称> tag<类别标签>,savePath<保存路径>)
User(name<QQ用户>,types[List<Type> types = new ArrayList<Type>()])
org.crawler.picture.dennisit.util包中定义XML读取工具.这里将XML的工具类贴出来(xml的东西不难,但是比较繁琐).系统中这里用单例设计模式
package org.crawler.picture.dennisit.util;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.crawler.picture.dennisit.entity.Type;
import org.crawler.picture.dennisit.entity.User;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.service.Constant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:45:35
*
* @function: 用户获取XML中配置的信息(单例设计模式)
*
*/
public class XMLConfigureUtil {
private static Log logger = LogFactory.getLog(XMLConfigureUtil.class); //日志应用对象
private Map<String, User> userMap = new HashMap<String, User>(); //界面初始化时用
private static XMLConfigureUtil xmlConfigure = new XMLConfigureUtil(); //单例创建的唯一对象
/**
* 构造私有化,保证只有一个对象
*/
private XMLConfigureUtil(){
readXML();
}
/**
* 单例对象获取方法
* @return
*/
public static XMLConfigureUtil getInstance(){
return xmlConfigure;
}
/**
*
* 检查配置文件路径并读取下载配置文件XML
*
*/
private void readXML() {
try {
//URL url = Toolkit.getDefaultToolkit().getClass().getResource(Constant.CONFIG_XML_WEBSITE_PATH);
URL url = getClass().getResource(Constant.CONFIG_XML_PATH);
if(null == url){
throw new PictureCrawlerException("未找到站点下载配置文件:" + Constant.CONFIG_XML_PATH);
}
readXML(url); //解析站点下载配置文件
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
/**
* 解析站点下载配置文件并进行数据对象化封装
* @param url
* @throws PictureCrawlerException
*/
private void readXML(URL url)throws PictureCrawlerException{
StringBuffer buffer = new StringBuffer("配置的下载用户QQ为:\n");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(url.toExternalForm());
NodeList typeNodes = doc.getElementsByTagName("user");
if(null==typeNodes || 0==typeNodes.getLength()){
throw new PictureCrawlerException("不存在userList标签");
}
for(int i=0; i<typeNodes.getLength(); i++){
Node typeNode = typeNodes.item(i); //获取user标签组
NamedNodeMap userListAttr = typeNode.getAttributes();
NodeList elements = typeNode.getChildNodes(); //获取user下所有的子节点
System.out.println(elements.getLength());
User user = new User(); //创建节点对象,进行物理数据到对象的填充
for(int k=0; k<userListAttr.getLength();k++){ //遍历user标签的属性
Node attribute = userListAttr.item(k);
String attributeName = attribute.getNodeName();
if(attributeName.equalsIgnoreCase("name")){ //对应user标签的name属性
user.setName(attribute.getNodeValue());
userMap.put(attribute.getNodeValue(), user); //按照name标签名-->name对应的User对象值进行存放到Map集合中
buffer.append("[").append(attribute.getNodeValue()).append("]").append("\n");
}else {
throw new PictureCrawlerException("不存在此类属性" + attributeName);
}
}
logger.info(buffer.toString()); //日志录入信息
for(int j=0; j<elements.getLength(); j++){
Node element = elements.item(j);
if(element instanceof Element){
Type type = new Type(); //创建类别对象,对应配置文件中的type标签
NamedNodeMap typeAttr = element.getAttributes(); //获取type标签中的所有属性
for(int k=0; k<typeAttr.getLength();k++){ //遍历属性
Node attribute = typeAttr.item(k); //依次获取属性
String attributName = attribute.getNodeName();
if(attributName.equalsIgnoreCase("name")){ //对应type标签的name属性
type.setName(attribute.getNodeValue());
}else if(attributName.equalsIgnoreCase("tag")){ //对应type标签的tag属性
type.setTag(attribute.getNodeValue());
}else if(attributName.equalsIgnoreCase("savePath")){ //type标签中不存在的属性显示不存在
type.setSavePath(attribute.getNodeValue());
}else{
throw new PictureCrawlerException("不存在属性:" + attributName);
}
}//遍历一行type标签结束
user.addType(type); //遍历完一个type标签,封装成对象,添加到user对象中
}
}//end for var j
}//end for var i
} catch (Exception e) {
throw new PictureCrawlerException(e.getMessage(),e);
}
}
/**
* 返回读取到的配置文件的封装的对象映射集合信息
* @return
*/
public Map<String, User> geUserMap() {
return this.userMap;
}
}
org.crawler.picture.dennisit.exception包下定义系统异常
package org.crawler.picture.dennisit.exception;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午06:58:23
*
* @function: TODO
*
*/
public class PictureCrawlerException extends Exception{
/**
*
*/
private static final long serialVersionUID = -8272962911990755136L;
public PictureCrawlerException(){
super();
}
public PictureCrawlerException(String message){
super(message);
}
public PictureCrawlerException(String message,Throwable cause)throws NoSuchMethodError {
super(message, cause);
}
}
images包下面是系统界面中用到的图片
辅助包与类基本上说完了,接下来的包与类是MVC的核心
org.crawler.picture.dennisit.view包下定义系统视图界面,界面可以自己根据需要去设计.但是最好设计成扩展性强的,死板的界面让人很不舒服.
这里贴一下我的系统界面的部分,其它的可以自己设计.但是这里注意的要点是如何将控制器植入到视图界面中
package org.crawler.picture.dennisit.view;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TextArea;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder;
import org.crawler.picture.dennisit.entity.Type;
import org.crawler.picture.dennisit.entity.User;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.service.Constant;
import org.crawler.picture.dennisit.service.ControlContext;
import org.crawler.picture.dennisit.util.XMLConfigureUtil;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:10:26
*
* @function: TODO
*
*/
public class PictureCrawlerJFrame extends JFrame{
/**
*
*/
private static final long serialVersionUID = 3112116020999565721L;
private ControlContext context; //控制器
private JComboBox userBox = new JComboBox(); //用户选择列表
private JComboBox typeBox = new JComboBox(); //模块选择列表
private JButton downloadButton; //下载按钮
private JButton exitButton; //系统退出按钮
private TextArea logTextArea; //日志显示屏幕
private JButton clearButton; //情况屏幕
private Map<String, User> userMap;
public PictureCrawlerJFrame(){
init();
}
public PictureCrawlerJFrame(ControlContext context){
init();
this.context = context;
}
/**
*
* 界面初始化
*
*/
public void init(){
this.setTitle(Constant.SYS_FRAME_TITLE);
this.setSize(Constant.SYS_WIND_WIDTH, Constant.SYS_WIND_HEIGHT);
this.setContentPane(createContentPanel());
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
context.exit(PictureCrawlerJFrame.this);
}
});
}
/**
* 创建内容面板的方法
* @return
*/
public JPanel createContentPanel(){
JPanel panel = new JPanel(new BorderLayout(3,1));
panel.add(creatSetInfoPanel(),BorderLayout.NORTH);
panel.add(createLogInfoPanel(),BorderLayout.CENTER);
panel.add(createClearPanel(),BorderLayout.SOUTH);
panel.setBorder(new EmptyBorder(15,4,15,4));
return panel;
}
/**
* 创建下载设置面板
* @return
*/
public JPanel creatSetInfoPanel(){
JPanel panel = new JPanel();
//从xml文件中获取配置信息,填充面板
setUserMap(XMLConfigureUtil.getInstance().geUserMap()); //获取用户集合
String choiceItem = getUserChoicedItem(getUserMap().keySet()); //获取选择的用户
userBox = setUsersComboBox(getUserMap().keySet(),choiceItem); //填充用户下来列表信息
userBox.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH, Constant.SYS_COMBOX_HEIGHT));
userBox.addActionListener(new ActionListener(){ //为用户下拉列表添加监听
@Override
public void actionPerformed(ActionEvent e) { //用户拉列表选定后,模块列表要更新
typeBox = setTypesComboBox(getUserMap().get(userBox.getSelectedItem()).getTypes());
}
});
typeBox = setTypesComboBox(getUserMap().get(choiceItem).getTypes());
typeBox.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH+30, Constant.SYS_COMBOX_HEIGHT));
downloadButton = new JButton(Constant.SYS_BTN_DCOPY_LABEL);
downloadButton.setPreferredSize(new Dimension(Constant.SYS_COMBOX_WIDTH+40,Constant.SYS_COMBOX_HEIGHT));
downloadButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
try {
context.download(PictureCrawlerJFrame.this);
} catch (PictureCrawlerException e1) {
e1.printStackTrace();
}
}
});
exitButton = createImageButton(" ","/images/exit.png");
exitButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
context.exit(PictureCrawlerJFrame.this);
}
});
panel.add(new JLabel(Constant.SYS_QQUSER_LABEL));
panel.add(userBox);
panel.add(new JLabel(Constant.SYS_TYPE_LABEL));
panel.add(typeBox);
panel.add(downloadButton);
panel.add(exitButton);
return panel;
}
/**
* 获取选择的QQ用户信息
* @param keySet
* @return
*/
public String getUserChoicedItem(Set<String> keySet) {
for(String key : keySet){
return key;
}
return null;
}
/**
*
* 获取选取的模块类别
* @return
*/
public Type getChoicedType() {
List<Type> types = getUserMap().get(userBox.getSelectedItem()).getTypes();
String selectType = (String) typeBox.getSelectedItem();
for (Type type : types) {
if (type.getTag().equals(selectType)) { //如果标签相同
return type;
}
}
return null;
}
/**
* 填充用户下列表数据
* @param keySet
* @param choiceItem
* @return
*/
private JComboBox setUsersComboBox(Set<String> keySet, String choiceItem) {
for (String key : keySet) {
userBox.addItem(key);
if (key.equals(choiceItem)) {
userBox.setSelectedItem(key);
}
}
return userBox;
}
/**
* 填充模块下拉列表
* @param types
* @return
*/
private JComboBox setTypesComboBox(List<Type> types) {
typeBox.removeAllItems();
for (Type type : types) {
typeBox.addItem(type.getTag()); //根据用户选择对应的模块标签
}
return typeBox;
}
/**
* 创建图片按钮的方法
* @param text
* @param url
* @return
*/
public JButton createImageButton(String text,String url){
final JButton button = new JButton(text);
button.setMargin(new Insets(0, 0, 0, 0)); // 设置按钮边框和标题文本之间的间隔
button.setContentAreaFilled(false); // 设置不绘制按钮的内容区域
button.setBorderPainted(false); // 设置不绘制按钮的边框
button.setHorizontalTextPosition(JButton.CENTER);
button.setIcon(new ImageIcon(this.getClass().getResource(url)));
button.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
setCursor(Cursor.HAND_CURSOR);
}
public void mouseExited(MouseEvent e) {
setCursor(Cursor.DEFAULT_CURSOR);
}
});
return button;
}
/**
*
* 创建下载信息输出面板
* @return
*/
public JPanel createLogInfoPanel(){
JPanel panel = new JPanel(new BorderLayout());
JScrollPane logScrollPane = new JScrollPane();
logTextArea = new TextArea("请选择站点跟栏目,执行图片下载");
logScrollPane.setViewportView(logTextArea);
......将textArea与控制器绑定
return panel;
}
/**
* 创建清楚屏幕面板
* @return
*/
public JPanel createClearPanel(){
JPanel panel = new JPanel();
...将按钮与控制器绑定
panel.add(clearButton);
return panel;
}
/**
* 创建系统托盘
* @throws PictureCrawlerException
*/
public void systemTray() throws PictureCrawlerException{
if (SystemTray.isSupported()) {
//创建系统托盘...
}
}
/**
* 显示窗体
*/
public void showView(){
this.setVisible(true);
}
/**
* 隐藏窗体
*/
public void unshowView(){
this.setVisible(false);
}
//getter and setter 提供控制器对视图界面数据获取
}
系统的视图介绍完毕.
接下来说说控制器
org.crawler.picture.dennisit.service包下2个文件Constant.java和ControlContext.java
Constant中存放的是系统中定义的字面值,可以归到视图,笔者设计时放在service下,
package org.crawler.picture.dennisit.service;
import java.awt.Toolkit;
import java.io.File;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-2 下午03:30:28
*
* @function: 系统常量定义
*
*/
public class Constant {
public static final String CONFIG_XML_PATH ="/userlist_config.xml"; //网址参数配置文件
public static final String FRAME_ICON_IMG = "/images/icon.gif"; //系统窗体图标
public static final String SYS_TRAY_IMG = "/images/icon.gif"; //系统托盘图标
public static final String SYS_SHOWWINDOW_LABEL = "显示窗体"; //系统托盘 显示窗体标签文字
public static final String SYS_EXITSYS_LABEL = "退出系统"; //系统托盘 退出系统标签文字
public static final String SYS_SET_LABEL = "系统设置"; //系统托盘 系统设置标签
public static final String SYS_USEHELP_LABEL = "系统帮助"; //系统托盘 系统帮助标签
public static final int SYS_WIND_WIDTH = 710; //系统界面设定 宽度
public static final int SYS_WIND_HEIGHT = 400; //系统界面设定 高度
public static final String SYS_FRAME_TITLE = "QQ空间备份软件"; //系统界面标签 frame文字标签
public static final String SYS_QQUSER_LABEL = "选择要备份的QQ用户:"; //系统界面标签 label文字标签
public static final String SYS_BTN_DCOPY_LABEL = "开始备份"; //系统界面标签 label文字标签
public static final String SYS_TYPE_LABEL = "模块:"; //系统界面标签 lebel文字标签
public static final String SYS_BTN_CLEAR_SCREEN = "清除日志显示屏幕"; //系统界面标签 按钮文字标签
public static final int SYS_COMBOX_WIDTH = 120; //系统界面标签 下拉列表宽度
public static final int SYS_COMBOX_HEIGHT = 30; //系统界面标签 下拉列表高度
public static final String SYS_ENCODING = "UTF-8"; //系统编码
public static final int TIMEOUT = 20000; //连接超时时间
public static final int SO_TIMEOUT = 20000; //数据传输超时
}
控制器主要是连接视图跟对应的业务逻辑的,获取数据,然后根据请求转到对应的业务Bean处理.
package org.crawler.picture.dennisit.service;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.Writer;
import java.util.List;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;
import org.crawler.picture.dennisit.action.AlbumDownloadAction;
import org.crawler.picture.dennisit.action.DownloadAction;
import org.crawler.picture.dennisit.action.DownloadActionFactory;
import org.crawler.picture.dennisit.entity.Album;
import org.crawler.picture.dennisit.exception.PictureCrawlerException;
import org.crawler.picture.dennisit.util.ConnectionUtil;
import org.crawler.picture.dennisit.view.PictureCrawlerJFrame;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-1 下午07:08:59
*
* @function: 控制器
*
*/
public class ControlContext {
private DownloadAction downloadAction ;
private DownloadActionFactory downloadActionFactory;
public ControlContext(){
downloadActionFactory = new DownloadActionFactory();
}
public ControlContext(DownloadAction downloadAction){
this();
this.setDownloadAction(downloadAction);
}
/**
* 启动窗体显示
* @param frame
*/
public void start(PictureCrawlerJFrame frame){
center(frame);
try {
frame.systemTray();
} catch (PictureCrawlerException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
/**
* 窗口居中显示,设定应该图标
* @param frame
*/
private void center(JFrame frame){
Image iconImg = Toolkit.getDefaultToolkit().getImage(getClass().getResource(Constant.FRAME_ICON_IMG)) ;
frame.setIconImage(iconImg);
frame.setLocationRelativeTo(null);
}
/**
* 退出系统
* @param frame
*/
public void exit(JFrame frame) {
String message = "退出系统应用?";
int opt = JOptionPane.showConfirmDialog(frame, message, "离开系统",JOptionPane.YES_NO_OPTION);
if(opt == JOptionPane.YES_OPTION) {
System.out.println("系统退出...");
frame.setVisible(false);
System.exit(0);
}
}
public void download(final PictureCrawlerJFrame frame) throws PictureCrawlerException {
System.out.println("选定的用户为:" + frame.getUserBox() + ",选定的用户的模块:" + frame.getChoicedType().getName());
System.out.println("相册保存路径:" + frame.getChoicedType().getSavePath());
String qqUser = frame.getUserBox(); // 获取选择的用户
String type = frame.getChoicedType().getName(); //获取选择的模块
String savePath = frame.getChoicedType().getSavePath();
if("相册".equals(type)){
//开始备份用户的相册
this.downloadAction = downloadActionFactory.getAlbumDownloadAction();
System.out.println("action创建为空:"+ (null==this.downloadAction));
downloadAlbum((AlbumDownloadAction)downloadAction, qqUser,savePath);
System.out.println("开始下载图片");
}else if("日志".equals(type)){
//开始备份用户的日志
this.downloadAction = downloadActionFactory.getDiaryDownloadAction();
...
调用日志备份的Action处理
}else if("说说".equals(type)){
this.downloadAction = downloadActionFactory.getStateDownloadAction();
...
调用说说备份的Action处理
//开始备份用户的说说
}else if("留言".equals(type)){
//开始备份用户的留言
this.downloadAction = downloadActionFactory.getNewsDownloadAction();
...
调用留言备份的Action处理
}else{
throw new PictureCrawlerException("配置参数不准确,[name=日志|说书|相册|留言]");
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
logAppender(frame);
}
}).start();
}
public void downloadAlbum(AlbumDownloadAction downloadAction,String qq, String savePath){
// 获取的所有相册
List<Album> albums = downloadAction.getAlbumsByAlubmSE1(qq);
if (albums == null || albums.size() == 0) {
albums = downloadAction.getAlbumsByAlubmSE2(qq);
}
if (albums == null || albums.size() == 0) {
System.out.println("没有获取到相册");
}
int len = albums.size();
System.out.println("相册信息获取成功,用户共有" + len + "个相册.");
for (int i = 0; i < len; i++) { // 考虑到相册数量不会很多,相册采用顺序下载,不使用异步下载
downloadAction.savePhoto(albums,savePath,i, qq);
albums.remove(i);
downloadAction.setCurIndex(0);
}
}
/**
* 输出日志到TextArea
*/
private void logAppender(PictureCrawlerJFrame frame) {
Logger root = Logger.getRootLogger();
Appender appender = root.getAppender("consoleA");
PipedReader reader = new PipedReader();
Writer writer = null;
try {
writer = new PipedWriter(reader);
} catch (IOException e) {
e.printStackTrace();
}
((WriterAppender) appender).setWriter(writer);
Scanner scanner = new Scanner(reader);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
frame.getLogTextArea().append("\n");
frame.getLogTextArea().append(line);
frame.getLogTextArea().append("\n");
line = null;
}
}
public void clearScreen(PictureCrawlerJFrame frame) {
System.out.println("清除屏幕...");
frame.getLogTextArea().setText("");
}
public void setDownloadAction(DownloadAction downloadAction) {
this.downloadAction = downloadAction;
}
public DownloadAction getDownloadAction() {
return downloadAction;
}
public void exit() {
String message = "退出系统应用?";
int opt = JOptionPane.showConfirmDialog(null, message, "离开系统",JOptionPane.YES_NO_OPTION);
if(opt == JOptionPane.YES_OPTION) {
System.out.println("系统退出...");
System.exit(0);
}
}
/**
* 显示窗体
* @param pictureCrawlerJFrame
*/
public void showView(PictureCrawlerJFrame pictureCrawlerJFrame) {
pictureCrawlerJFrame.setVisible(true);
}
}
控制器的部门介完毕
接下来就是核心的业务Bean.工厂类根据请求创建对应的请求Action.
package org.crawler.picture.dennisit.action;
/**
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-1-2 下午11:51:44
*
* @function: TODO
*
*/
public class DownloadActionFactory {
/**
* 相册下载处理Action
* @return
*/
public DownloadAction getAlbumDownloadAction(){
return new AlbumDownloadAction();
}
/**
* 日志下载处理Action
* @return
*/
public DownloadAction getDiaryDownloadAction(){
return new DiaryDownloadAction();
}
/**
* 留言下载处理Action
* @return
*/
public DownloadAction getNewsDownloadAction(){
return new NewsDownloadAction();
}
/**
* 说说下载处理Action
* @return
*/
public DownloadAction getStateDownloadAction(){
return new StateDownloadAction();
}
}
对相册下载模块Action代码如下.
package org.crawler.picture.dennisit.action; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; 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; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.crawler.picture.dennisit.entity.Album; import org.crawler.picture.dennisit.entity.Photo; import org.crawler.picture.dennisit.service.Constant; /** * * @version : 1.1 * * @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a> * * @since : 1.0 创建时间: 2013-1-2 下午11:36:06 * * @function: 相册下载备份类 * */ public class AlbumDownloadAction extends DownloadAction { private static final String albumbase1 = "http://alist.photo.qq.com/fcgi-bin/fcg_list_album?uin=";// 如果没有设置密保的相册是通过这个地址访问的 private static final String albumbase2 = "http://xalist.photo.qq.com/fcgi-bin/fcg_list_album?uin=";// 设置密保的相册是通过这个地址访问的 // private static final String photobase = private static final String photobase1 = "http://plist.photo.qq.com/fcgi-bin/fcg_list_photo?uin="; private static final String photobase2 = "http://xaplist.photo.qq.com/fcgi-bin/fcg_list_photo?uin="; private static List<Album> albums; // 获取的所有相册 private int curIndex = 0; // 每个相册当前正在下载的图片指针 public List<Album> getAlbumsByAlubmSE1(String qq) { return getAlbums(qq, albumbase1); } public List<Album> getAlbumsByAlubmSE2(String qq) { return getAlbums(qq, albumbase2); } /** * * 获取用户的所有相册 * * @param qq * @param url * @return */ public List<Album> getAlbums(String qq, String url) { List<Album> result = new ArrayList<Album>(); HttpClient client = new HttpClient(); String getUri = url + qq + "&outstyle=2"; // outstyle!=2服务器将以xml的形式返回结果, HttpMethod method = new GetMethod(getUri); // 这里只以简单文本解析提取相关信息,不做xml解析了. method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,Constant.SYS_ENCODING); int status = 0; try { status = client.executeMethod(method); if (status != HttpStatus.SC_OK) { System.err.println("发生网络错误!"); return null; } } catch (HttpException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } InputStream is = null; BufferedReader br = null; InputStreamReader isr = null; List<String> ids = new ArrayList<String>(); List<String> names = new ArrayList<String>(); List<Integer> totals = new ArrayList<Integer>(); try { is = method.getResponseBodyAsStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String temp = null; while ((temp = br.readLine()) != null) { if (temp.contains("\"id\" :")) { String id = temp.substring(temp.indexOf("\"id\" :") + 8, temp.length() - 2); ids.add(id); } if (temp.contains("\"name\" :")) { String name = temp.substring( temp.indexOf("\"name\" :") + 10, temp.length() - 3); names.add(name); } if (temp.contains("\"total\" :")) { String total = temp .substring(temp.indexOf("\"total\" :") + 10, temp .length() - 1); totals.add(Integer.parseInt(total)); } if (temp.contains("\"left\" :")) { break; } } } catch (IOException e) { e.printStackTrace(); } finally { method.releaseConnection(); try { br.close(); isr.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } for (int i = 0; i < ids.size(); i++) { Album album = new Album(ids.get(i), names.get(i), totals.get(i)); result.add(album); } return result; } /** * 获取一个相册的图片信息 * * @param album * 相册的信息 * @param qq * qq * @param phpUrl * 地址 * @return */ public List<Photo> getPhotoByAlbum(Album album, String qq, String phpUrl) { List<Photo> result = new ArrayList<Photo>(); HttpClient client = new HttpClient(); System.out.println("相册名字:" + album.getId()); String getUri = phpUrl + qq + "&albumid=" + album.getId() + "&outstyle=json"; HttpMethod method = new GetMethod(getUri); method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,Constant.SYS_ENCODING); int status = 0; try { status = client.executeMethod(method); if (status != HttpStatus.SC_OK) { System.err.println("发生网络错误!"); return null; } } catch (HttpException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } InputStream is = null; BufferedReader br = null; InputStreamReader isr = null; List<String> names = new ArrayList<String>(); List<String> urls = new ArrayList<String>(); try { is = method.getResponseBodyAsStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String temp = null; boolean sign = false; while ((temp = br.readLine()) != null) { int len = temp.length(); if (temp.contains("\"pic\" : [")) { sign = true; } if (sign && temp.contains("\"name\" :")) { String name = temp.substring( temp.indexOf("\"name\" :") + 10, len - 2); names.add(name); } if (temp.contains("\"url\" :")) { String url = temp.substring(temp.indexOf("\"url\" :") + 9, len - 3); urls.add(url); } if (temp.contains(" ],")) { break; } } } catch (IOException e) { e.printStackTrace(); } finally { method.releaseConnection(); try { br.close(); isr.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } for (int i = 0; i < names.size(); i++) { Photo photo = new Photo(); photo.setName(names.get(i)); photo.setUrl(urls.get(i)); photo.setAlbum(album); result.add(photo); } return result; } /** * 通过uri下载远程图片到本地 * * @param * @param 要保存的图片名 * (非路径) */ public void saveImgFromUrl(String savePath, Photo photo, String qq) { URL imgurl = null; BufferedInputStream bis = null; OutputStream os = null; try { String phpUrl = photo.getUrl(); phpUrl = phpUrl.replace("\\", ""); imgurl = new URL(phpUrl); bis = new BufferedInputStream(imgurl.openStream()); byte[] buffer = new byte[512]; File path = new File(savePath + "//" + qq + "//" + photo.getAlbum().getName().trim()); if (!path.exists()) { path.mkdirs(); } os = new FileOutputStream(new File(path, photo.getName().trim() + ".jpeg")); int len = 0; while ((len = bis.read(buffer)) > 0) { os.write(buffer, 0, len); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { bis.close(); os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 下载一个相册的图片 * * @param index * 相册序号 */ public void savePhoto(final List<Album> albums,final String savePath, final int index, final String qq) { Album album = albums.get(index); if (album.getName().indexOf("微博") >= 0) { System.out.println("微博相册不下载"); return; } List<Photo> photosTemp = this.getPhotoByAlbum(album, qq, photobase1); if (photosTemp == null || photosTemp.size() == 0) { photosTemp = this.getPhotoByAlbum(album, qq, photobase2); } if (photosTemp == null || photosTemp.size() == 0) { System.out.println("相册信息为空"); return; } else { final List<Photo> photos = photosTemp; final int maxThreadCnt = 10; // 每个相册最多开启10个线程进行下载 final int total = album.getImgCount(); int realThreadCnt = total >= maxThreadCnt ? maxThreadCnt : total; // 实际下载一个相册的线程数 /** * 线程驱动下载任务 * * @author wensefu.jerry.Ling<br/> * wrote on 2011-1-29 */ /* * 初始化各线程状态 此处给每个线程分配一个下载起始点 */ for (int i = 0; i < realThreadCnt; i++) { startDownloadThread(total, photos,i, index, savePath, qq); } } } private void startDownloadThread(final int total,final List<Photo> photos,final int id, final int index, final String savePath,final String qq) { new Thread(new Runnable() { int id; // 线程标识 int pindex = id + 1;// 下载的图片指针 public void run() { while (getCurIndex() <= total - 1) { int temp = pindex; pindex = getCurIndex(); setCurIndex(getCurIndex() + 1); Photo photo = photos.get(temp); System.out.println("线程" + (index + 1) + "_" + id + "开始下载第" + (index + 1) + "个相册第" + (pindex + 1) + "张图片..."); saveImgFromUrl(savePath, photo, qq); System.out.println("线程" + (index + 1) + "_" + id + "完成第" + (index + 1) + "个相册第" + (pindex + 1) + "张图片下载"); } } }).start(); } ... getter and setter }
至此, 整个项目的MVC应用分析结束,不过Swing编程在企业开发中很少用,所以建议初学的朋友没必要深究.
转载请注明出处[http://www.cnblogs.com/dennisit/archive/2013/01/03/2843506.html]