转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/76912307

本文出自【我是干勾鱼的博客

之前在文章《基于Java的门户网站管理系统——JEECMS源码版的搭建步骤》中讲述了jeecms的搭建,那个时候还是V6版本,现在已经是V8版本了,功能又比之前增强了不少。

在文章《jeecms系统使用介绍——jeecms中的内容、栏目、模型之间的关系》中讲述了“内容”、“栏目”、“模型”、“内容模型”、“栏目模型”这几者之间的关系。

而在文章《使用Lucene对doc、docx、pdf、txt文档进行全文检索功能的实现》中,又讲解过如何使用Lucene对word、pdf、txt这些文档内容建立索引进行全文检索。

今天就将这几篇文章内容结合起来,讲解一下如何通过二次开发实现对word、pdf、txt等上传附件的全文检索。

1 原有的针对文章的全文检索方式

1.1文章提交

正常情况下,发表一篇文章时,点击“提交”的时候,如图:

点击“提交”的时候是将文章所有字段的信息提交到后台,其实就是将这篇文章对应的“内容模型”中的数据都提交到后台了(“内容模型”的含义可以参考《jeecms系统使用介绍——jeecms中的内容、栏目、模型之间的关系》)。

这个过程比较重要的就是文章内容了,这里是通过百度的ueditor编辑器编辑了内容,然后将其提交的,这部分内容正文的数据其实是保存到了表:

jc_content_txt

的“txt”字段之中,如图:

而其它几个字段:

txt1
txt2
txt3

这些字段中是没有保存内容的,它们是附加字段,默认没有被使用,留由用户在二次开发时自行设置。这也为我们处理对附件文件的全文检索提供了可能。

文章内容的保存是在包:

com.jeecms.cms.action.admin.main

里的类:

ContentAct
@RequiresPermissions("content:o_save")
    @RequestMapping("/content/o_save.do")
    public String save(Content bean, ContentExt ext, ContentTxt txt,
            Boolean copyimg,Integer[] channelIds, Integer[] topicIds,
            Integer[] viewGroupIds,
            String[] attachmentPaths, String[] attachmentNames,
            String[] attachmentFilenames, String[] picPaths, String[] picDescs,
            Integer channelId, Integer typeId, String tagStr, Boolean draft,
            Integer cid, Integer modelId,Short charge,Double chargeAmount,
            Boolean rewardPattern,Double rewardRandomMin,
            Double rewardRandomMax,Double[] rewardFix, HttpServletRequest request,HttpServletResponse response, ModelMap model) 

1.2 建立索引

文章在“ContentAct”类的“save”方法中保存过程中,系统会单独开启一个线程,在类:

com.jeecms.cms.staticpage.ContentStatusChangeThread

里的方法:

public void run()

中。逐层进入到类:

com.jeecms.cms.lucene.LuceneContent

里面的方法:

public static Document createDocument(Content c)

在这个方法里面,会把文章的相关信息拿出来,包括文章的正文内容,存入Lucene索引里面去。

2 对文章中上传的附件进行全文检索

2.1 实现思路

我们先来说一下实现的思路。有几点我们是可以确认的:

  • 1,在文章中上传的附件是保存在了服务器的磁盘上,有一个磁盘路径。

  • 2,文章提交时,所有信息都会传给后台服务器。

  • 3,后台服务器对文章内容有一个保存的过程。

  • 4,保存过程中还会再单独建一个线程,用来对文章内容建立索引。

那么,根据我们的需求,要达到目的,我们完全可以这样来实现

  • 1,在文章中上传的附件时,将上传到服务器的附件路径及文件名称先在浏览器的前台页面保存一下。

  • 2,文章提交时,将附件路径及名称也传递到后台服务器。

  • 3,在后台服务器保存文章内容的过程中,将附件信息也保存一下,保存到数据库表“jc_content_txt”的字段“txt1”里面。这里相当于使用了“txt1”这个默认字段。

  • 4,对文章内容创建索引时,除了对文章正文的html内容建立索引,还根据附件路径及名称,找到附件文件并进行读取,对这些附件的内容建立索引。

2.2 上传时保存附件路径及名称到文章页面

编辑文章的页面在:

{jeecms}/WebRoot/WEB-INF/jeecms_sys/content/add.html

这个页面,我们在其中加入一个区域,保存上传附件的服务器路径及其名称。注意附件上传并保存到服务器之后其名称就变了,所以需要保存一下。

如图所示“input”为新加入部分,“value”值会存放内容。每次上传附件时会将信息保存的这个“input”的“value”里面,那么还要在上传操作中加入保存的方法。

在内容中上传附件,如图:

点击附件图标,进入附件上传界面,如图:

先点击“点击选择文件”按钮,选择了附件,如图:

然后点击“开始上传”,这个过程会将文件上传到服务器的磁盘文件中。点击上传时的操作在:

{jeecms}/WebRoot/thirdparty/ueditor/dialogs/attachment/attachment.js

中的方法:

uploader.on

里面,这是百度“ueditor”编辑器,我们做些修改之后内容如下:

在原有内容里,加入了每次上传时先将所上传的附件文件的路径及名称保存到前台页面的功能。详细代码如下:

uploader.on('uploadSuccess', function (file, ret) {
                var $file = $('#' + file.id);
                try {
                    var responseText = (ret._raw || ret),
                        json = utils.str2json(responseText);
                    if (json.state == 'SUCCESS') {
                        _this.fileList.push(json);
                        $file.append('<span class="success"></span>');

                        //alert(file + "----" + ret);

                        var attachTemp = $("input[name='attachmentsName']", window.parent.document).val();
                        if( (attachTemp == null) || (attachTemp == "")) {
                            $("input[name='attachmentsName']", window.parent.document).val(ret.url);
                        } else {
                            $("input[name='attachmentsName']", window.parent.document).val(attachTemp + ',' + ret.url);
                        }

                        //alert('attachmentsName=' + $("input[name='attachmentsName']", window.parent.document).val());

                    } else {
                        $file.find('.error').text(json.state).show();
                    }
                } catch (e) {
                    $file.find('.error').text(lang.errorServerUpload).show();
                }
            });

可以把对alert的注释删除掉,然后看一下每次记录的内容。这里上传三个文件,看一下效果:

一个doc,一个docx,一个pdf,这3个上传文件的服务器相对路径及新名称都得到了。

3 提交文章时将附件路径传到后台

之前已经说了,点击了“提交”按钮后,后台对文章内容的保存是在类:

com.jeecms.cms.action.admin.main.ContentAct

的方法:

public String save

中进行的,然后在对其建立索引。为了能够对前台传过来的附件内容也能建立索引,要对“save”方法加点内容,如下:

4 对附件创建索引

万事俱备,只欠东风。该得到的信息都得到了,最后我就需要对上传的附件创建索引了。之前讲过创建索引是在类:

com.jeecms.cms.lucene.LuceneContent

里面的方法:

public static Document createDocument(Content c)

进行的,这里我们还在这里面进行。

这里面有一个默认处理模型项“txt1”的地方,如下:

if (!StringUtils.isBlank(c.getTxt1())) {
            doc.add(new Field(CONTENT1, c.getTxt1(), Field.Store.NO,
            Field.Index.ANALYZED));
        }

我们对它进行修改,在这个区域里面,将上传的附件按照类型的不同分别创建索引,内容如下:

if (!StringUtils.isBlank(c.getTxt1())) {

            InputStream in = null;
            InputStreamReader reader = null;

            try {
                //读取txt1中保存的json串
                JSONObject json = new JSONObject(c.getContentTxt().getTxt1());
                //读取json串中保存的应用服务器在操作系统上的绝对路径
                String realPath = json.getString("realPath");
                //读取json串中保村的上传附件的相对路径及名称
                String attachmentsNameArrays = json.getString("attachmentsNameArrays");
                //得到索引文件数组
                String []arr = attachmentsNameArrays.split(",");

                for (int j = 0; j < arr.length; j++) {
                    //将要搜索word文件的地方
                    String dateDir = realPath + "\\" + arr[j];
                    //读取当前这个附件文件
                    File file = new File(dateDir);

                    //输出这个文件是第几个附件
                    System.out.println("j=" + j);
                    //输出这个文件的路径及名称
                    System.out.println("file=" + file);

                    //获取文件名称
                    String fileName = file.getName();
                    //获取文件后缀名
                    String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();

                    //输出文件名称
                    System.out.println("fileName=" + fileName);
                    //输出文件名称
                    System.out.println("fileType=" + fileType);

                    //创建当前附件文件的输入流
                    in = new FileInputStream(file);  

                    //在当前附件文件的后缀名不为空的情况下进行操作,创建索引的文件包括doc、docx、pdf、txt
                    if (fileType != null && !fileType.equals("")) {

                        if (fileType.equals("doc")) {
                            //创建斌保存doc文件索引

                            //读取doc文件
                            WordExtractor wordData = new WordExtractor(in);

                            //创建Field对象,并放入lucene的document对象doc中
                            doc.add(new Field(CONTENT1, wordData.getText(), Field.Store.NO,
                                    Field.Index.ANALYZED));
                            //输出当前操作的文件名称
                            System.out.println("注意:已为文件“" + fileName + "”创建了索引");
                            wordData.close();

                        } else if ( fileType.equals("docx")) {
                            //创建斌保存docx文件索引

                            //读取docx文件
                            XWPFWordExtractor wordData = new XWPFWordExtractor(new XWPFDocument(in));

                            //创建Field对象,并放入lucene的document对象doc中
                            doc.add(new Field(CONTENT1, wordData.getText(), Field.Store.NO,
                                    Field.Index.ANALYZED));
                            //输出当前操作的文件名称
                             System.out.println("注意:已为文件“" + fileName + "”创建了索引");
                            wordData.close();

                        }else if ( fileType.equals("pdf")) {
                            //创建斌保存pdf文件索引

                            //读取pdf文件
                            PDFParser parser = new PDFParser(in);
                            parser.parse();
                            PDDocument pdDocument = parser.getPDDocument();
                            PDFTextStripper stripper = new PDFTextStripper();
//                          String result = stripper.getText(pdDocument);

                            //创建Field对象,并放入lucene的document对象doc中
                            doc.add(new Field(CONTENT1, stripper.getText(pdDocument), Field.Store.NO,
                                    Field.Index.ANALYZED));
                            //输出当前操作的文件名称
                            System.out.println("注意:已为文件“" + fileName + "”创建了索引");
                            pdDocument.close();

                        } else if ( fileType.equals("txt") ) {
                            //创建斌保存txt文件索引

                            //读取txt文件
                            //建立一个输入流对象reader
                            reader = new InputStreamReader(in);
                            //建立一个对象,它把文件内容转成计算机能读懂的语言
                            BufferedReader br = new BufferedReader(reader);
                            String wordData = "";
                            String line = null;

                            //一次读入一行数据
                            while ((line = br.readLine()) != null) {
                                wordData += line;
                            }  

                            //创建Field对象,并放入lucene的document对象doc中
                            doc.add(new Field(CONTENT1, wordData, Field.Store.NO,
                                    Field.Index.ANALYZED));
                            //输出当前操作的文件名称
                            System.out.println("注意:已为文件“" + fileName + "”创建了索引");

                        } else {

                        }

                    }

                }

            } catch (JSONException e) {

                e.printStackTrace();

            } catch (FileNotFoundException e) {

                e.printStackTrace();

            } catch (IOException e) {

                e.printStackTrace();

            } finally {

                if (in != null) {
                    try {  

                        in.close();

                    } catch (IOException e) {  

                        e.printStackTrace();  

                    }
                }
                if (reader != null) {

                    try {

                        reader.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

这里面对doc、docx、pdf、txt几种类型的文件分别创建了索引,具体的调用方式可以参考《使用Lucene对doc、docx、pdf、txt文档进行全文检索功能的实现》这篇文章,此处就不赘述了。

5 搜索

至此,功能事先就完成了。我们新建一篇文章,上传一个附件进去。然后搜索一个只在附件中才出现过的内容,进行搜索。比如新建一篇文章,如图:

里面上传了一个附件,内容为:

里面的内容很简单。现在我们在前台页面上搜索“上传附件”这几个汉字,比如输入“使用介绍”,如图:

然后点击键盘回车或鼠标点击右侧的搜索按钮,得到搜索结果如下:

能够看到,文章的正文内容里直接看是没有“使用介绍”这几个字的,但搜索这几个字还是把这篇文章搜了出来,说明我们建立的索引生效了。

至此本文的功能也就讲完了。

jeecms系统使用介绍——通过二次开发实现对word、pdf、txt等上传附件的全文检索的更多相关文章

  1. web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)

    web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等) 前言 我们在做web系统中,导出也是很常用的一个功能,如果每一个数据列表都要对应写一个导出的方法不太现实 ...

  2. jeecms系统使用介绍——jeecms中的内容、栏目、模型之间的关系

    转载:https://blog.csdn.net/dongdong9223/article/details/76578120 jeecms是一款很不错的cms产品,之前在文章<基于Java的门户 ...

  3. iOS开发——网络篇——NSURLSession,下载、上传代理方法,利用NSURLSession断点下载,AFN基本使用,网络检测,NSURLConnection补充

    一.NSURLConnection补充 前面提到的NSURLConnection有些知识点需要补充 NSURLConnectionDataDelegate的代理方法有一下几个 - (void)conn ...

  4. 循序渐进学.Net Core Web Api开发系列【5】:文件上传

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍通 ...

  5. 通用的web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)

    前言 我们在做web系统中,导出也是很常用的一个功能,如果每一个数据列表都要对应写一个导出的方法不太现实.现在就想设计一个共通的功能来实现这个导出. 需求分析 在开始之前我们先要明白我们要实现怎样一个 ...

  6. ios开发之网络数据的下载与上传

    要实现网络数据的下载与上传,主要有三种方式 > NSURLConnection  针对少量数据,使用“GET”或“POST”方法从服务器获取数据,使用“POST”方法向服务器传输数据; > ...

  7. iOS开发进阶 - 使用shell脚本自动打包上传到fir.im上-b

    用fir.im测试已经好长时间了,感觉每次打包上传都很麻烦,想着是不是可以用脚本自动打包,在网上搜了一下确实有,下面总结一下如何使用脚本自动打包上传到fir.im,以及打包过程中遇到的问题和解决办法 ...

  8. python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

    昨日内容回顾 基于对象的跨表查询 正向查询:关联属性在A表中,所以A对象找关联B表数据,正向查询 反向查询:关联属性在A表中,所以B对象找A对象,反向查询 一对多: 按字段:xx book ----- ...

  9. 模拟文件上传(二):使用apache fileupload组件进行文件上传

    其中涉及到的jar包: jsp显示层: <%@ page language="java" import="java.util.*" pageEncodin ...

随机推荐

  1. Robot framework(RF)学习基础

    1.框架特点 2.安装 3.基本内容 RF框架是通用的测试框架,一直由诺西网络维护. 易于使用 采用表格法 可以使用关键字驱动,数据驱动和行为驱动开发完成.(keyword-driven,data-d ...

  2. R语言低级绘图函数-abline 转载

    abline 函数的作用是在一张图表上添加直线, 可以是一条斜线,通过x或y轴的交点和斜率来确定位置:也可以是一条水平或者垂直的线,只需要指定与x轴或y轴交点的位置就可以了 常见用法: 1)添加直线 ...

  3. Linux——vim/vi 简单学习笔记

    Vim/Vi是一个功能强大的全屏幕文本编辑器,是Linux/UNIX上最常用的文本编辑器,它的作用是建立.编辑.显示文本文件.Vim/Vi 没有菜单,只有命令. 早前也用过Vim变过C++/C的代码, ...

  4. Yandex.Algorithm 2011 Round 2 D. Powerful array 莫队

    题目链接:点击传送 D. Powerful array time limit per test 5 seconds memory limit per test 256 megabytes input ...

  5. Redis之无序集合类型命令

    Redis 集合(Set) Redis 的 Set 是 String 类型的无序集合.集合成员是唯一的,这就意味着集合中不能出现重复的数据. Redis 中集合是通过哈希表实现的,所以添加,删除,查找 ...

  6. C#两种数据类型

    C#的两种类据类型:值类型和引用类型   什么是值类型,什么是引用类型 概念:值类型直接存储其值,而引用类型存储对其值的引用.部署:托管堆上部署了所有引用类型. 引用类型:基类为Objcet 值类型: ...

  7. WPF使用Webbrowser操作网页的主要代码

    1,引用mshtml.dll using mshtml; 2,获取元素属性值 IHTMLDocument2 doc2=(IHTMLDocument)webbrowser1.Document; IHTM ...

  8. codeforces 555b//Case of Fugitive// Codeforces Round #310(Div. 1)

    题意:有n-1个缝隙,在上面搭桥,每个缝隙有个ll,rr值,ll<=长度<=rr的才能搭上去.求一种搭桥组合. 经典问题,应列入acm必背300题中.属于那种不可能自己想得出来的题.将二元 ...

  9. Intent在Activity之间传值的几种方式

    发这篇博客主要讲一下Android中Intent中如何传值的几种方法: 1:基本数据类型,包含了Java八种基本数据类型和CharSequece文本2:八种数据类新对应数组和CharSequece文本 ...

  10. TCP客户端与服务器的实现

    为了更容易理解,我们举一个小例子来说明服务器与客户端之间的连接过程. 有一个饭店,饭店里有服务员,服务员用于招待客人 特别要注意的是:要记住相关函数的各个参数都是什么,什么时候返回SOCKET_ERR ...