结合Java实现的一个腾讯空间备份器谈谈MVC思想在Swing桌面项目中的应用

java

       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包下定义系统异常

View Code

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.

View Code

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 + "&album>;

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]

 

以上是 结合Java实现的一个腾讯空间备份器谈谈MVC思想在Swing桌面项目中的应用 的全部内容, 来源链接: utcz.com/z/393066.html

回到顶部