实训项目


layout: post
title: 实训项目
date: 2020-12-6
author: XiaoJia849
categories:
- 开发部
tags:
- 后端
- springboot
- 文件处理


项目实训已经度过3周,三周内被迫学会了springboot 和mybatis的基本使用,以下是我的一点总结。以及对文件的操作记录。

  • Maven
  • Mybatis
  • Springboot
  • 上传excel与解析
  • 上传任意类型文件且保存
  • 浏览器下载文件
  • java处理word模板

Maven

Maven是个好东西,如果在配置pom.xml时不经常爆红的话。
Maven在我的理解中是一个jar包管理库,使用Maven让我无需导入jar包,如果需要使用这个jar包时,只需要在pom.xml内写入依赖即可,非常方便。

Mybatis

Mybatis内写sql语句调用数据库的资源,把所有涉及与数据库交互的过程全部封装在mapper内的xml文件中,但是对于初次接触MyBatis的我而言不是很友好。当我需要写嵌套查询时,发现此时仅仅是更改sql语句只会引发一串报错,这也就意味着,当sql语句跳出了简简单单的一层插入,更新,查询时,难度大幅度上升了。其实只是我太菜了,要写嵌套查询还是很方便的。

例子

    <insert id="createPro">
        insert into pro (Pid,Pname,Pcontent,Pnum)
        values (#{Pid},#{Pname},#{Pcontent},#{Pnum})
    </insert>
     <select id="getMaxid" resultType="java.lang.Integer">
        select MAX(Pid)
        from pro;
    </select>
     <select id="getPro" resultType="com.demo.model.Pro">
        select
            Pid,Pname,Pcontent,Pnum,
        from pro;
    </select>
     <update id="setPid">
        update stu_team_pro
        set Pid=#{pid}
        where GroupId=#{groupId};
    </update>

和sql里面写嵌套查询没啥区别,我也不知道为什么我在网上搜到的那么复杂ㄟ( ▔, ▔ )ㄏ
总而言之,感谢队友 (_)

<insert id="ReplyGrade">
        Update tea_ans
        set Grade=#{Grade}
        where Tid=#{Tid} and ReplyId in(
            select ReplyId
            from scen
            where GroupId=#{GroupId}
        );
    </insert>

Springboot

Springboot的亮点在于5层框架,把每层的功能分的十分细致。

  • model层描述对象的属性。
  • dao层与Mybatis打交道,里面写满各种与数据库相关的方法。
  • service层则可以将dao层的方法组合起来使用,当要处理的问题简单时,其实并不需要service层。
  • control层用于和前端进行交互,接收数据,调用dao层或者service层的方法进行处理,处理后将结果返回前端。
  • config层我只知道可以作为拦截器限制control层与前端的交互。

此外,springboot的优点还在于可以清晰的让每个函数只做一件事,比如说:
函数A要更改数据库,函数B要实现把数据库中的数据拿到前端展示的作用
可是我们要实现的功能是界面上更改数据库传出请求后马上返回到这个页面,要将AB两个函数的功能按顺序使用,那么可以在A函数结束后 return "redirect:/B" ,这样就OK了

之前采用MVC框架写过前后端的代码,那个时候并不知道springboot这么香的存在,哎

上传excel与解析

功能

前端上传excel文件,后端实现获取excel内容,并将对应的内容存入数据库内

要点

  1. 上传文件的 jsp要进行以下修改
<meta http-equiv="X-UA-Compatible" content="multipart/form-data;charset=utf-8">
        <form action="AdminEXC" method="post" enctype="multipart/form-data">
                 <input type="file" name="exc" accept=".xls,.xlsx" enctype="multipart/form-data" />
                 <input id="button" type="submit" value="确 定" />
        </form>

在jsp的head内和form内都明确上传的数据类型是multipartFile,且限制上传的文件类型为excel

  1. Maven导包使支持POI且2007之后的excel文件处理

在pom.xml内写入以下内容

    <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml</artifactId>
          <version>3.15</version>
      </dependency>
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-scratchpad</artifactId>
          <version>3.8</version>
       </dependency>

写入以上之后才能使用XSSF

  1. control层内代码实现
   @PostMapping("AdminEXC")
    public String export(@RequestParam("exc") MultipartFile file,
                       HttpServletRequest request, HttpServletResponse response) {
        try {
            // @RequestParam("file") MultipartFile file 是用来接收前端传递过来的文件
            // 1.读取文档file并且把数据流形式存放在inputStream内
            InputStream inputStream = file.getInputStream();
            //定义工作簿,把数据流以excel文件的形式组织起来,形成工作簿
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook(inputStream);
            //定义工作表,工作簿第0页也就是第一张sheet
            XSSFSheet xssfSheet;
            xssfSheet = xssfWorkbook.getSheetAt(0);
            //已知excel第0列内容是专业
            //要获取不重复的专业列我采用放入map的方式
            Map<String, Integer> map= new HashMap<String,Integer>();
            //定义标题行,第0行为标题行,index = 0
            XSSFRow titleRow = xssfSheet.getRow(0);
            //xssfSheet无法直接获得一列的元素,所以只能用行的for循环
            //获取专业列也即map的key 列
            for (int rowIndex = 1; rowIndex < xssfSheet.getPhysicalNumberOfRows(); rowIndex++) {
                XSSFRow xssfRow = xssfSheet.getRow(rowIndex);
                if (xssfRow.getCell(3) == null) {
                    continue;
                }else{
                    map.put(xssfRow.getCell(3).getStringCellValue(),1);
                }
            }
            //xssfSheet.getPhysicalNumberOfRows()代表这个工作表的行数,并不一定就等于有文字的部分的行数,所以在进行for循环时,判断对应的值是否为null时十分有必要的
            
            
            //根据key的排列对value进行从上到下的标号
            //标号后存入数据库内
            int i=0;
            //将专业相关信息插入dept表内
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                i=i+1;
                entry.setValue(i);
                //一个数据库插入函数
                //把key作为sdept i作为Sdid 插入
                proDao.Insdid(i,entry.getKey());
            }


            //循环取每行的数据,完善学生相关信息
            for (int rowIndex = 1; rowIndex < xssfSheet.getPhysicalNumberOfRows(); rowIndex++) {
                XSSFRow xssfRow = xssfSheet.getRow(rowIndex);
                if (xssfRow.getCell(3) == null) {
                    continue;
                }else{
                    String sid=xssfRow.getCell(0).getStringCellValue();
                    String sname=xssfRow.getCell(1).getStringCellValue();
                    String spwd=sid.substring(0,3)+sid.substring(5,8);
                    Integer sdid=map.get(xssfRow.getCell(3).getStringCellValue());
                    proDao.Instu(sid,sname,spwd,sdid);
                }
            //以上部分为基操,不做描述
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "Adminfunc";
    }

上传任意类型文件且保存

上传文件的 jsp要进行以下修改

<meta http-equiv="X-UA-Compatible" content="multipart/form-data;charset=utf-8">
        <form action="Proupload" method="post" enctype="multipart/form-data">
                <select  name="pid">
                            <option value="1">xx</option>
                            <option value="2">hh</option>
                 </select>
                 <input type="file" name="xfile" enctype="multipart/form-data" />
                 <input id="button" type="submit" value="确 定" />
        </form>

在jsp的head内和form内都明确上传的数据类型是multipartFile

   @PostMapping("Proupload")
    public String Upload(HttpServletRequest request,@RequestParam("xfile") MultipartFile file){
        try{
            //获取题目id
            int pid=Integer.valueOf(request.getParameter("pid"));
            //得到文件名
            String filename = file.getOriginalFilename();
            //得到文件类型
            String extFileName = filename.substring(filename.lastIndexOf("." ) +1,filename.length());
            //例如上传文件xx.txt
            //filename="xx.txt"
            //extFileName="txt"
            
            //获取当前所在文件夹的绝对路径realPath
            String realpath=request.getSession().getServletContext().getRealPath("/");
            //略微修改一下,得到我将把上传的文件夹存入的路径 ——filePath
            String filePath=realpath.substring(0,realpath.length()-7)+"resources\\file\\";
            String path = filePath + filename;
            //以上两者拼接得到一个完整的文件存入的路径
            //在这个路径上创建文件
            File dest = new File(path);
            if (!dest.getParentFile().exists()){
                //若此路径不存在则创建目录
                dest.getParentFile().mkdir();
            }
            //将file文件完全复制存入到dest这个文件内
            file.transferTo(dest);
            //把对应的数据库信息存入
            proDao.Inurl(pid,path);
        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "Teafunc";
    }

浏览器下载文件

  1. jsp内
    <form action="download" method="post">
            <input type="submit" value="下载">
    </form>
  1. pom.xml内补充依赖
     <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-engine</artifactId>
        </dependency>
  1. control层
   @PostMapping("download")
    public static void downFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //等待被下载的文件的绝对路径
        String url="F:\\ne\\AC自动机&TireTree.docx";
        //该文件的名字
        String filename="AC自动机&TireTree.docx";
        //找到该文件
        File file=new File(url);
        //  文件存在才下载
        if (file.exists()) {
            OutputStream out = null;
            FileInputStream in = null;
            try {
                // 把文件转化为数据流存储在in内
                in = new FileInputStream(file);
                // 告诉浏览器下载的方式以及一些设置
                // 解决文件名乱码问题,获取浏览器类型,转换对应文件名编码格式,IE要求文件名必须是utf-8, firefo要求是iso-8859-1编码
                //获取浏览器信息
                String agent = request.getHeader("user-agent");
                if (agent.contains("FireFox")) {
                //如果是火狐浏览器,则需要转编码
                    filename = new String(filename.getBytes("UTF-8"), "iso-8859-1");
                } else {
                //其他的全部设置为UTF-8形式
                    filename = URLEncoder.encode(filename, "UTF-8");
                }
                // 设置下载文件的mineType,告诉浏览器下载文件类型
                String mineType = request.getServletContext().getMimeType(filename);
                //设置相应类型与文件类型一致(因为要下载这个文件)
                response.setContentType(mineType);
                // 设置一个响应头,无论是否被浏览器解析,都下载
                response.setHeader("Content-disposition", "attachment; filename=" + filename);
                // 将要下载的文件内容通过输出流写到浏览器
                out = response.getOutputStream();
                //把输入流的内容全部复制到输出流
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = in.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
                //复制结束后实现了输出一个一模一样的文件
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            }
        }
    }

例子

java处理word模板

  1. jsp内
<meta http-equiv="X-UA-Compatible" content="multipart/form-data;charset=utf-8">
<form action="getGrade" method="post"enctype="multipart/form-data">
        <input id="button" type="submit"  value="生成成绩"/>
</form>
  1. pom.xml内
<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>
  1. control层

已知我的模板文件在resources/file/下 stugrade.docx
要在同目录下创建文件xx4.docx


stugrade.docx
    @PostMapping("getGrade")
    public static String getGrade(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取当前项目的绝对地址
        String realpath=request.getSession().getServletContext().getRealPath("/");
        String filePath=realpath.substring(0,realpath.length()-7)+"resources\\file\\";
        //模板文件地址,下面过程不会更改模板文件
        String inputUrl = filePath + "stugrade.docx";
        //新生产的文件
        String outputUrl=filePath+"xx4.docx";
        //在map内按照模板对应位置的英文key补充填写内容value
        Map<String,String> map=new HashMap<>();
        map.put("Pname","hhh");
        map.put("Tname","hhh");
        map.put("student","hhh");
        map.put("tea1","hhh");
        map.put("grade1","hhh");
        map.put("tea2","hhh");
        map.put("grade2","hhh");
        map.put("time","hhh");
        map.put("place","hhh");
        map.put("grade","hhh");
        try {
            //获取word文档解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
            //获取文章
            Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
            while (itPara.hasNext()) {
                //获取段落
                XWPFParagraph paragraph = (XWPFParagraph) itPara.next();
                //获取段落内的最小结构run
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    //获取文字
                    String text = run.getText(0);
                    if (text != null) {
                        boolean isSetText = false;
                        //遍历map寻找key
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            String key = entry.getKey();
                            if (text.indexOf(key) != -1) {// 在配置文件中有这个关键字对应的键
                                isSetText = true;
                                Object value = entry.getValue();
                                if (value instanceof String) {
                                    // 文本替换
                                    if (text.contains(key)) {
                                        text = text.replace(key, value.toString());
                                    }
                                }
                            }
                        }
                        if (isSetText) {
                            run.setText(text, 0);
                        }
                    }
                }
            }
            //创建输出文件
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            //把document的内容写入到stream对应的文件file内
            document.write(stream);
            stream.close();
            } catch (IOException e) {
                    // TODO Auto-generated catch block
                e.printStackTrace();
            }
        return "Adminfunc";
    }

备注

request.getHeader("useradgent")解释
mimetype解释

参考

解析excel
文件上传
解决springMVC文件上传报错: The current request is not a multipart request
从后端下载文件到浏览器
获取本地文件的几种方式
获取项目根目录
试图打开word报错
word替换关键字
poi的jar包参考
XWPF文本替换
参考网址整理时,还有遗漏的,也有些部分是我查看后发现不适用的就没放上来

项目

这个是我们小组6个人的作品,如果允许的话,项目源码将在这个实训结束后上传github网址用于分享 不了,还是算了吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容