关于RSS订阅功能的实现与初步认识

                     起初会有各种的规范,也就是要实现一个符合rss规范的xml文档,这个文档会在火狐、QQ浏览器上自动解析成一个文章列表,大概就是这个意思,好像还有个rss阅读器什么的,反正这个RSS订阅功能在2000年初的时候好像流行了一段时间,但是后来就不火了,所以网上可供参考的内容也不多吧。。。少说废话 ,直接code吧。

                             这是规范。。。。

                   关于RSS订阅功能的实现与初步认识

     关于RSS订阅功能的实现与初步认识

             

            关于RSS订阅功能的实现与初步认识


                      关于RSS订阅功能的实现与初步认识

   

                        关于RSS订阅功能的实现与初步认识


              好了,以上就是我当初拿到的规范和要实现的内容模板,直接看code。

    

             我是用的web框架是sping+springmvc+hibernate,数据库的话是mysql.


            package com.hjhz.app.web.action.admin.rss;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


import com.hjhz.app.common.Global;
import com.hjhz.app.common.WebRootPath;
import com.hjhz.app.db.dao.JksbMessageDAO;
import com.hjhz.app.db.po.JksbMessage;
import com.sun.syndication.feed.rss.Channel;
import com.sun.syndication.feed.rss.Content;
import com.sun.syndication.feed.rss.Description;
import com.sun.syndication.feed.rss.Item;
import com.sun.syndication.feed.rss.Source;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.WireFeedOutput;


/**
 * 
 * @author YGA
 *
 */
@Controller
@RequestMapping(value="/RSS")
public class SiteRSSAction extends ParameterizableViewController{

  private static final String COULD_NOT_GENERATE_FEED_ERROR = "Could not generate feed";
  
  @Inject
  private JksbMessageDAO JksbMessageDAO;
  
  @RequestMapping(value="/getRss")
  public String createRSS(HttpServletRequest request,HttpServletResponse response) throws Exception{
   String rootPath = WebRootPath.getPath();
String path = rootPath + Global.UPLOAD_FILE_RSS_PATH;//上传路径
try {
          Channel channel = getChannel(request);
          OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream(path),"UTF-8");//文件编码格式为utf-8
//           Writer w=new FileWriter(out);
          WireFeedOutput outs = new WireFeedOutput();
          outs.output(channel, o);//先上传至服务器做保存
          o.close();
          /*****************************************************************/
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          dbf.setValidating(false);
          DocumentBuilder db = dbf.newDocumentBuilder();
          Document doc = db.parse(new FileInputStream(new File(path)));//再解析上传的RSS的xml文件为Document格式
          Element element = doc.getDocumentElement();//获得根节点
          NodeList nlist=element.getChildNodes();//获得根节点下的所有子节点
          for(int i=0;i<nlist.getLength();i++){
          Node node1 = nlist.item(i);
          if("channel".equals(node1.getNodeName())){
          NodeList nodeDetail = node1.getChildNodes();
          for(int j=0;j<nodeDetail.getLength();j++){
          Node node2=nodeDetail.item(j);
          
          if("title".equals(node2.getNodeName())){//处理channel下的title节点
          String rssContent=node2.getTextContent();
          NodeList nodeDetails=node2.getChildNodes();
          for(int f=0;f<nodeDetails.getLength();f++){
          Node nodes=nodeDetails.item(f);
          if("#text".equals(nodes.getNodeName())){//存在之前的乱码文本节点
          nodes.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在title节点中追加<![CDATA[...]]>封装正文内容
          node2.appendChild(cdata);//追加
          }
          if("link".equals(node2.getNodeName())){//处理channel下的link节点
          String rssContent=node2.getTextContent();
          NodeList nodeDetails=node2.getChildNodes();
          for(int f=0;f<nodeDetails.getLength();f++){
          Node nodes=nodeDetails.item(f);
          if("#text".equals(nodes.getNodeName())){//存在之前的乱码文本节点
          nodes.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在link节点中追加<![CDATA[...]]>封装正文内容
          node2.appendChild(cdata);//追加
          }
          if("description".equals(node2.getNodeName())){//处理channel下的description节点
          String rssContent=node2.getTextContent();
          NodeList nodeDetails=node2.getChildNodes();
          for(int f=0;f<nodeDetails.getLength();f++){
          Node nodes=nodeDetails.item(f);
          if("#text".equals(nodes.getNodeName())){//存在之前的乱码文本节点
          nodes.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在description节点中追加<![CDATA[...]]>封装正文内容
          node2.appendChild(cdata);//追加
          }
          if("item".equals(node2.getNodeName())){
          NodeList nodeDetail2 = node2.getChildNodes();
          for(int k=0;k<nodeDetail2.getLength();k++){
          Node node3=nodeDetail2.item(k);
          
          if("title".equals(node3.getNodeName())){//处理title节点
          String rssContent=node3.getTextContent();
          NodeList nodeDetail3=node3.getChildNodes();
          for(int f=0;f<nodeDetail3.getLength();f++){
          Node node4=nodeDetail3.item(f);
          if("#text".equals(node4.getNodeName())){//存在之前的乱码文本节点
          node4.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在item节点中追加<![CDATA[...]]>封装正文内容
          node3.appendChild(cdata);//追加
          }
          if("link".equals(node3.getNodeName())){//处理link节点
          String rssContent=node3.getTextContent();
          NodeList nodeDetail3=node3.getChildNodes();
          for(int f=0;f<nodeDetail3.getLength();f++){
          Node node4=nodeDetail3.item(f);
          if("#text".equals(node4.getNodeName())){//存在之前的乱码文本节点
          node4.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在link节点中追加<![CDATA[...]]>封装正文内容
          node3.appendChild(cdata);//追加
          }
          if("description".equals(node3.getNodeName())){//处理description节点
          String rssContent=node3.getTextContent();
          NodeList nodeDetail3=node3.getChildNodes();
          for(int f=0;f<nodeDetail3.getLength();f++){
          Node node4=nodeDetail3.item(f);
          if("#text".equals(node4.getNodeName())){//存在之前的乱码文本节点
          node4.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在description节点中追加<![CDATA[...]]>封装正文内容
          node3.appendChild(cdata);//追加
          }
          if("content:encoded".equals(node3.getNodeName())){//处理content:encoded节点
          String rssContent=node3.getTextContent();
          rssContent=newString(rssContent.toString());//去掉所有的style="..."
          NodeList nodeDetail3=node3.getChildNodes();
          for(int f=0;f<nodeDetail3.getLength();f++){
          Node node4=nodeDetail3.item(f);
          if("#text".equals(node4.getNodeName())){//存在之前的乱码文本节点
          node4.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在content:encoded节点中追加<![CDATA[...]]>封装正文内容
          node3.appendChild(cdata);//追加
          }
          if("pubDate".equals(node3.getNodeName())){//处理pubDate节点
          String rssContent=node3.getTextContent().replaceAll("GMT","+0800");
          NodeList nodeDetail3=node3.getChildNodes();
          for(int f=0;f<nodeDetail3.getLength();f++){
          Node node4=nodeDetail3.item(f);
          if("#text".equals(node4.getNodeName())){//存在之前的乱码文本节点
          node4.setTextContent("");//将已经存在的文本内容置为空
          }
          }
          CDATASection cdata = doc.createCDATASection(rssContent);//在pubDate节点中追加<![CDATA[...]]>封装正文内容
          node3.appendChild(cdata);//追加
          }
          }
          }
          }
          }
          }
          prettyPrint(doc,response);//解析完之后再返回给客户端
          /*****************************************************************/
//           String feedType = request.getParameter(FEED_TYPE);//null
//           feedType = (feedType!=null) ? feedType : _defaultFeedType;
//           channel.setFeedType(feedType);//rss_2.0
//           response.setContentType(MIME_TYPE);
//           WireFeedOutput out = new WireFeedOutput(); 
//           out.output(channel,response.getWriter());//向发出请求的用户输出该RSS(xml格式)
      }
      catch (FeedException ex) {
          String msg = COULD_NOT_GENERATE_FEED_ERROR;
          response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,msg);
      }
 return null;
  }


        /**
         * 进行rss文件组装
         * @param request
         * @return
         * @throws IOException
         * @throws FeedException
         */
   protected Channel getChannel(HttpServletRequest request) throws IOException,FeedException {
      //feed就是channel
       Channel channel = new Channel("rss_2.0");  
       channel.setTitle("aaaaaaaa");//网站标题
       channel.setDescription("这是描述。。。。");//网站描述
       channel.setLink("www.baidu.com");//网站主页链接
       channel.setEncoding("UTF-8");//RSS文件编码      
       channel.setLanguage("zh_cn");//RSS使用的语言      
//        channel.setTtl(5);//time to live的简写,在刷新前当前RSS在缓存中可以保存多长时间(分钟)      
       List<Item> items = new ArrayList<Item>();//这个list对应rss中的item列表  
       Item item=null;
       Iterator<JksbMessage> iterator = setIterator(request);
       while(iterator.hasNext()){//遍历
        JksbMessage jm=(JksbMessage)iterator.next();
        item = new Item();//新建Item对象,对应rss中的<item></item>
        item.setTitle(jm.getTitle());//对应<item>中的<title></title>      
       item.setLink("http://localhost:8080/aa/Rss/rssMessageDetial.jhtml?mid="+jm.getId()+"&s=cm");//新闻具体详情,走的是cc提供的模板
       try {
item.setPubDate(dateFormat(jm.getCreateTime()));//这个<item>对应的发布时间 
} catch (ParseException e) {
e.printStackTrace();
}     
       Source s=new Source();
       s.setValue(jm.getInfoSource());//来源
       item.setSource(s);
       //新建一个Description,它是Item的描述部分      
       Description description = new Description(); 
       description.setValue(clearIllegalCharacter(delLastImg(jm.getContent())));//<description>中的内容,过滤掉最后一张的广告和二维码图片
       item.setDescription(description);//添加到item节点中      
       Content content=new Content();//正文
       content.setType("text/html");
       content.setValue(clearIllegalCharacter(delLastImg(jm.getContent())));//过滤掉最后一张的广告和二维码图片
       item.setContent(content);
       items.add(item);//代表一个段落<item></item>,      
       }
       channel.setItems(items);      
      return channel;
  }
   //从数据库中获得的文章
   private Iterator<JksbMessage> setIterator(HttpServletRequest request){
Long currentTime=System.currentTimeMillis();//当下时间毫秒数
Long biginTime=currentTime-(7*24*3600*1000);//获得提前一周的当下毫秒 
return JksbMessageDAO.pageByTime(biginTime,currentTime).iterator();//所有的新闻数据
   }
 
   public static final void prettyPrint(Document xml,HttpServletResponse response) throws Exception {
       Transformer tf = TransformerFactory.newInstance().newTransformer();
       tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
       tf.setOutputProperty(OutputKeys.INDENT, "yes");
       Writer out = new StringWriter();
       tf.transform(new DOMSource(xml), new StreamResult(out));
    String rss = out.toString();//XML文本字符串
       response.setCharacterEncoding("utf-8");
       response.setContentType("text/xml;charset=utf-8");
       response.setHeader("Cache-control", "no-cache");
       PrintWriter o = response.getWriter();
       o.println(rss);
       out.close();
       o.close();
  }
 
   public static Date dateFormat(Long t) throws ParseException{
Date d=new Date(t);
SimpleDateFormat sf=new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z",Locale.ENGLISH);
String dd=sf.format(d);
Calendar ca=Calendar.getInstance();
ca.setTime(sf.parse(dd));
ca.add(Calendar.HOUR_OF_DAY,8);
Date c=ca.getTime();
return c;
  }
 
   public static String newString(String originalString){
   String content = originalString;
   // 正则表达式
   String regEx = "style=\"(.*?)\"";
   Pattern p = Pattern.compile(regEx);
   Matcher m = p.matcher(content);
   String okContent = null;
   if (m.find()) {
       okContent = m.replaceAll("");
   }else{
    okContent=content;
   }
   return okContent;
  }
   
   public static String delLastImg(String originalString){
    String content = originalString;
    int lastedImgIndex=content.lastIndexOf("<img");
    if(lastedImgIndex==-1){
    return content;
    }else{
    String newContent=(String) content.subSequence(0, lastedImgIndex);
    return newContent;
    }
   }
   
   public static String clearIllegalCharacter (String str){
    char [] xmlChar = str.toCharArray();  
       for (int i=0; i < xmlChar.length; ++i) {  
       if (xmlChar[i] > 0xFFFD)   
       {   
          //直接替换掉0xb   
           xmlChar[i] =' ';  
       }   
       else if (xmlChar[i] < 0x20 && xmlChar[i] != 't' & xmlChar[i] != 'n' & xmlChar[i] != 'r')  
       {  
          //直接替换掉0xb  
           xmlChar[i] =' ' ;  
       }  
       }
    return String.valueOf(xmlChar);
   }
}


把主要的思路给大家简单描述一下吧

      第一步就是要封装成channel对象,这个channel就是符合rss规范的那个xml文档所必须的!规范内容代码中很详细也贴出了规范模板自己去看吧。。。

      第二步我是先将一开始封装的xml上传到指定的服务器先做一下保存,也是为了后期可以知道用户是否使用了该功能。

      第三步就是再讲刚上传的xml文档再读取回来,读取城document格式的,因为文档规范要求  ‘

文章正文,为HTML格式,需用<![CDATA[ ]]>封装并

包含所有图片的链接,但不要包含样式信息。

该元素定义在命名空间(Namespace)Content​中,

需要在rss元素中包含相应的声明。

’这个<![CDATA[]]>莱德来封装内容或者介绍的,先期在代码中是怎么也实现不了,不是单纯的字符串拼接呦,也是小弟不才,菜鸟一枚,还没发现更好的方法吧,所以也是基于这一点,就先上传到服务器做保存然后在读取回来Documeng形式再动态加入<![CDATA[]]>标签来封装以前的描述或者内容。

      第四步就是把解析完的xml再一次已模板要求的格式返回给浏览器客户端了。

      

      哈哈哈,大致就是这么个流程吧,看了一天的文档,动手写了两天也总算是实现了功能了吧。希望看到这篇文章的人有所收获,也更希望看到这篇文章的大牛能够给出更为详细的指导和指正!