首页 > 编程语言 >JavaWeb笔记(项目案例-部门员工管理、文件上传)

JavaWeb笔记(项目案例-部门员工管理、文件上传)

时间:2024-12-19 13:30:47浏览次数:5  
标签:文件 实体类 JavaWeb image 笔记 上传 originalFilename String

项目环境搭建

  • 资料

image-20230806154503315

image-20230806154522661

  • 接口文档在Day10

三层架构

  • controller : 负责接受请求 ,处理响应
  • service : 逻辑处理 ,为了增强程序灵活性 ,方便层与层之间解耦 ,我们会采用面向接口的方式 ,还需要准备业务层的接口 ,mapper本身就是一个接口
  • mapper : 数据访问操作

环境搭建

image-20230803191100352

image-20230803191530278

image-20230803191735318

部门实体类

image-20230803193807061

员工实体类

image-20230803193822019

数据库和实体类字段名一致问题

第一种解决办法

  • 实体类当中的属性与表中的字段是一一对应的 ,在实体类中 ,采用的是驼峰命名 ,表中的字段是下划线风格

  • 也就是在配置文件里面开启驼峰命名法

image-20230803195300519

mybatis默认是属性名和数据库字段名一一对应的,即
数据库表列:user_name
实体类属性:user_name

但是java中一般使用驼峰命名
数据库表列:user_name
实体类属性:userName

在Springboot中,可以通过设置map-underscore-to-camel-case属性为true来开启驼峰功能。

第二种解决办法

image-20230803194928201

1、@TableField(value = "email")//指定数据库表中字段名

如果数据库和实体类的字段名不一致,可以使用@TableField注解指定数据库表中字段名。
    
 2、@TableField(exist = false)//数据库表中不存在的数据,在实体类中指定。

如果数据库表中不存在字段,在实体类中使用@TableField注解指定。

例如:数据库表中没有address字段,可以在该字段上方使用@TableField(exist = false)来指定。

3、@TableField(select = false)//查询时不返回该字段的值

如果不想被查询出来该字段,可以使用@TableField注解来隐藏该字段的查询结果。

例如:不想被查出来password字段的值,就可以使用@TableField注解。

开发规范

image-20230803200545350

image-20230803200906705

  • restful是一种软件架构风格 是一种约定 ,不是规定 ,可以打破

  • 模块中功能通常使用复数 ,也就是加s的格式来描述 ,表示此类资源 ,而非单个资源 ,比如 : users ,emps ,books

  • 返回格式

image-20230803201317545

开发流程

image-20230803201827062

部门管理

image-20230803220812878

查询部门

  • 流程

image-20230803202724578

  • 以后调试不用往控制台sout ,直接可以使用日志

问题代码 : 下面的代码使用put ,delete ,get等方法都可以访问 ,不符合我们的要求

image-20230803203221480

代码改进

image-20230803210640439

  • 因为controller需要调用service ,所以我们直接面向接口编程 ,注入一个部门Service的接口对象

前后端联调

  • 对于前端的请求 ,URL里面的api ,就表示要访问Tomcat服务器的8080端口 ,这个是前端规定好的
  • 也就是如果我们修改了后端的接口 ,那么前端就不会正常访问到接口 ,这都源于对于接口文档的遵守

image-20230803212227026

image-20230803212519815

删除部门

image-20230803212815335

mapper接口中的方法

image-20230803213305150

新增部门

image-20230803213819889

  • 注意补充属性 ,前端在页面中需要用户输入的是部门名称 ,但是其实还返回了时间数据 ,也就是需要我们来补充时间属性 ,接受时间参数

DeptServiceImpl.add()

image-20230803214755906

  • dept表有四个字段 ,id不需要插入 ,加入约束之后 ,出现蓝色标识
  • 表中最后那个字段是要反馈给前端页面展示的 ,最后操作时间

image-20230803214735653

image-20230803220618699

  • dept表中name字段 ,name’也是一个key

image-20230803220106915

image-20230803220425888

image-20230803220432602

  • 注意新增部门的sql ,传过来的是对象 ,但是sql中的参数可以直接写属性名字

image-20230805092038668

修改部门

  • 还是注意sql语句的写法 ,传过来的是部门对象 ,但是可以直接写对象里面的属性

image-20230805094450721

员工管理

分页查询

image-20230805094810854

select * from emp limit 0,5;
0不是页数 ,是根据页数算出来的
表示从原数据库的第一页的第0条数据(索引为1)开始查 ,一共返回5条数据

image-20230805095148625

  • 起始索引计算公式 起始索引 = (页码-1)*每页展示记录数

  • 前端要进行分页查询 ,返回页码和每页记录数 ,后端需要返回结果 ,结果封装在实体类里面 ,

image-20230805095527253

image-20230805100013562

  • 注意pageBean实体类的属性名字要和接口文档对应 ,因为这个属性名是要交给前端解析的 ,他和其他的实体类不同 ,这个实体类是要被返回给前端的

image-20230805100333628

image-20230805100413061

mapper

image-20230805103024435

controller

contoller层加上getMapping ,注意默认参数可以使用@requestParms(default指定)

image-20230805103056074

service

image-20230805103115709

插件 PageHelper

image-20230805111232339

  • 引入依赖

image-20230805111300537

<!--pageHelper分页查询依赖-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

mapper

image-20230805111548639

service

image-20230805112146116

controller不用改

条件分页查询

image-20230805113212530

image-20230805113236263

  • 上面在sql中使用了concat连接字符串 ,目的是将来传参的时候还可以使用#{}

image-20230805113347168

controller

时间类型用的是LocalDate ,注解是@LocalDateFormat ,后面的格式pattern是接口文档中给出的

image-20230805114047056

mapper

image-20230805114112264

service ,注意Short大写

image-20230805115731874

image-20230805115809082

删除员工

image-20230805173807170

  • 批量删除使用的是in关键字

image-20230805173927119

image-20230805175637218

mapper.xml

image-20230805175654360

新增员工

  • 有一条要求就是默认密码是123456 ,这个默认密码我们不需要前端传递 ,只需要在数据库写个默认值就可以了

image-20230805180330686

  • 接口文档中的规定 ,以json格式传递给后端

image-20230805180509115

  • 思路

image-20230805180639489

  • 注意事项

页面上的归属部门 ,是前端根据后端反馈的数据查询出来的 ,我们可以看到刷新页面 ,出现两个请求 ,第二个depts就是前端请求查看部门

image-20230805182135521

文件上传

本地存储

步骤
  • 前端三要素里面的enctype是传输文件的格式 ,普通默认的编码格式是不适合传输大型二进制数据

image-20230805194351667

  • 前端页面

image-20230805195059468

  • 注意表单项上的名称要和方法中形参的名称一致才可以正确接收image-20230805195453670 ,但是如果表单项上的名字和方法中形参名字不一样 ,可以加一个注解image-20230805195502709

  • 文件上传 ,提交方式是post

  • 后台接收文件使用api :MultiPartFile

  • MultiPartFile中的方法image-20230805212130479

debug断点调试
  • 第一步 : 在要断点调试的地方打上断点

image-20230805200342206

  • 第二步 : 启动debug

image-20230805200430970

  • 第三步 : 打开网页选择文件并且点击提交

image-20230805200533713

  • 第四步 : 后台服务器已经接收到前端请求并出现调试页面

image-20230805200605411

  • 页面反馈三个临时文件 ,因为表单中就有三项 ,所以这里也是三个临时文件

image-20230805200928111

image-20230805200908124

  • 第五步 : 放行

image-20230805200859472

  • 只要这次请求响应完成 ,临时文件会被自动删除 ,所以除了接收文件 ,我们还需要把文件保存起来,保存就需要用到api
  • 规定文件存储路径 ,注意后面的两条杠

image-20230805205227900

@Slf4j
@RestController
public class UploadController {


    @PostMapping("/upload")
    public Result upload(String username , Integer age , MultipartFile image) throws Exception {
        log.info("文件上传:{},{},{}",username,age,image);
        //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //将文件存储在服务器的磁盘目录中,后面需要指定文件名
        image.transferTo(new File("D:\\testCode\\image\\"+originalFilename));

        return Result.success();
    }

}
代码演示
  • 调用api ,transferTo ,会把接受到的磁盘文件转存到某一个磁盘文件里面 ,里面需要什么对象就给什么对象

image-20230805202112829

  • postman测试文件上传 ,选择file

image-20230805203219306

生成随机文件名(UUID)
  • 方式一
public String file( MultipartFile file, HttpServletRequest request) throws IOException {
        //获取文件保存位置的的绝对路径,实际开发中,建议直接使用一个服务器的绝对地址
        String realPath = request.getServletContext().getRealPath("/WEB-INF/upload/");
        String originalFilename = file.getOriginalFilename();
        //获取上传文件的后缀名
        String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
        // 获取服务器中存储的文件名
        String s = UUID.randomUUID().toString().replaceAll("-", "") + substring;
        // 创建文件实例
        File file1 = new File(realPath, s);
        // 如果文件目录不存在,创建目录
        if(file1.getParentFile().exists()){
            file1.getParentFile().mkdirs();
            System.out.println("创建目录"+file1);
        }
// 写入文件
        file.transferTo(file1);
  • 方式二
@Slf4j
@RestController
public class UploadController {


    @PostMapping("/upload")
    public Result upload(String username , Integer age , MultipartFile image) throws Exception {
        log.info("文件上传:{},{},{}",username,age,image);
        //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //构造唯一的文件名
        //表示先获取文件名 ,然后找到最后一个点所处的位置(是一个索引),因为最后一个点后面是文件的扩展名
        // ,然后从这个地方开始截取,直到字符串的尾部
        int index = originalFilename.lastIndexOf(".");
        String extname = originalFilename.substring(index);
        String newFileName = UUID.randomUUID().toString()+extname;
        log.info("获取到的新的文件名: {}",newFileName);

        //将文件存储在服务器的磁盘目录中,后面需要指定文件名
        image.transferTo(new File("D:\\testCode\\image\\"+newFileName));

        return Result.success();
    }

}
文件大小过大报错
  • 当上传一个超过1兆的图片时候发现报错

image-20230805210635810

  • 解决办法 : 在yml文件中配置

image-20230805211043320

image-20230805211435153

问题及解决
  • 本地存储的图片没办法在网页上被访问到

  • 本地中保存大量图片 ,占据很大空间

  • 本地磁盘如果坏了 ,数据全部丢失

  • 可以自己搭建文件存储服务 ,FastDFS分布式文件系统 ,MinIO对象存储服务搭建集群解决问题

  • 云服务 : 阿里云 腾讯云 百度云 华为云

完整代码

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
<!--action指的是表单往哪里提交 ,method指的是表单的提交方式是post ,enctype是来设置表单的编码格式-->
    <form action="/upload" method="post" enctype="multipart/form-data">
        姓名: <input type="text" name="username"><br>
        年龄: <input type="text" name="age"><br>
        头像: <input type="file" name="image"><br>
        <input type="submit" value="提交">
    </form>

</body>
</html>
//UploadController
import com.itheima.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;

@Slf4j
@RestController
public class UploadController {
    
    //本地存储
    @PostMapping("/upload")
    public Result upload(String username , Integer age , MultipartFile image) throws Exception {
        log.info("文件上传:{},{},{}",username,age,image);
        //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //构造唯一的文件名
        //表示先获取文件名 ,然后找到最后一个点所处的位置(是一个索引),因为最后一个点后面是文件的扩展名
        // ,然后从这个地方开始截取,直到字符串的尾部
        int index = originalFilename.lastIndexOf(".");
        String extname = originalFilename.substring(index);
        String newFileName = UUID.randomUUID().toString()+extname;
        log.info("获取到的新的文件名: {}",newFileName);

        //将文件存储在服务器的磁盘目录中,后面需要指定文件名
        image.transferTo(new File("D:\\testCode\\image\\"+newFileName));

        return Result.success();
    }

}

阿里云OSS

步骤
  • 通用思路

image-20230805212728797

image-20230805212735770

  • 使用步骤

image-20230805212918141

AccessKey ID
LTAI5t73p3RUbGNX8yyMQEPG

AccessKey Secret
9MnecnXDtSafObvshHq1ddDBZb6l9a
  • 获取AccessKey并创建

image-20230805213816660

  • 获取SDK文档 ,点击SDK示例

image-20230805214046466

image-20230805214305004

  • 复制代码到自己的项目里面
  • 节点地址

image-20230805214921639

配置访问凭证

Java配置访问凭证

  • 配置长期访问凭证

image-20230806102821775

集成

image-20230806103541483

image-20230806103734091

  • 给工具类上方添加@Component注解

image-20230806105330275

utils.AliOSSUtils

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;

/**
 * 阿里云 OSS 工具类
 */
@Component
public class AliOSSUtils {

    private String endpoint = "https://oss-cn-beijing.aliyuncs.com";
    private String accessKeyId = "LTAI5t73p3RUbGNX8yyMQEPG";
    private String accessKeySecret = "9MnecnXDtSafObvshHq1ddDBZb6l9a";
    private String bucketName = "web-tliasbucket";

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}
  • 拼接访问路径

image-20230806104404895

//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
//就是加上bucket名字 ,后面再跟上文件的名称

image-20230806104317641

  • 接口文档中上传文件参数信息

image-20230806104817624

image-20230806104823380

  • 注意方法的形参名字要和接口文档上面的参数名称image保持一致

UploadController

image-20230806105701921

@Autowired
private AliOSSUtils aliOSSUtils;

//阿里云存储
@PostMapping("/upload")
public Result upload(MultipartFile image) throws Exception {
    log.info("文件上传, 文件名{}",image.getOriginalFilename());
    //调用阿里云OSS工具类进行文件上传
    String url = aliOSSUtils.upload(image);
    log.info("文件上传完成 ,文件访问的url: {}",url);
    return Result.success(url);
}
  • postman测试

image-20230806105853841

  • 前后端联调

选择图片后 ,自动回显

image-20230806110226184

修改员工

流程

  • 流程

点击编辑之后 ,先要通过这条数据的id将结果查询出来 ,然后展示在表单页面上 ,这个过程就叫查询回显image-20230806114101930

修改数据之后 ,需要更新数据库信息

数据回显

image-20230806114426085

image-20230806114901075

修改员工

  • 补充信息如下

  • 更新员工信息的时候CreateTime不需要更新 ,只需要更新UpdateTime

  • 动态sql ,只需要更新被修改过的字段值 ,没有修改的就不需要更新

image-20230806115731495

  • 这里我们没有对gender判断是不是空字符串 ,是因为Gender不可能为空字符串 ,他是一个Short类型
  • mybatis动态sql使用xml文件实现
<!-- 修改 -->
<update id="update">
    update emp
    <set>
        <if test="username != null and username != ''">
            username = #{username},
        </if>
        <if test="password != null and password != ''">
            password = #{password},
        </if>
        <if test="name != null and name != ''">
            name = #{name},
        </if>
        <if test="gender != null">
            gender = #{gender},
        </if>
        <if test="image != null and image != ''">
            image = #{image},
        </if>
        <if test="job != null">
            job = #{job},
        </if>
        <if test="entrydate != null">
            entrydate = #{entrydate},
        </if>
        <if test="deptId != null">
            dept_id = #{deptId},
        </if>
        <if test="updateTime != null">
            update_time = #{updateTime}
        </if>
    </set>
    where id = #{id}
</update>

image-20230806120707135

  • 测试

image-20230806121015359

image-20230806121022797

配置文件

properties

image-20230806151810474

image-20230806151818678

image-20230806152747782

yml

image-20230806152858988

@ConfigurationProperties

  • 实现配置类中的属性对实体类实现自动注入 ,但前提是实体类的属性名和配置文件中的后缀名保持一致

image-20230806153307929

  • 还需要指定前缀 ,在@ConfigurationProperties(prefix=“aliyun.oss”)注解里指定

image-20230806153719997

image-20230806153707000

image-20230806154036198

image-20230806154009638

标签:文件,实体类,JavaWeb,image,笔记,上传,originalFilename,String
From: https://blog.csdn.net/weixin_73749601/article/details/144583810

相关文章

  • JavaWeb笔记(项目案例-登录功能、登录校验、异常处理)
    资料登录功能步骤DeptController的请求路径都是/Dept开头的EmpController的请求路径都是/Emp开头的UploadController是用来文件上传的那么LoginController就是用来登录的注意功能走的其实是emp实体类注意mapper接口中的方法名问题,login是登录,是一个业务方法,但是......
  • Java笔记(反射、动态代理、注解)
    Java笔记(反射、动态代理、注解)反射概念获取class对象的三种方式获取一个类的全类名全类名:包名+类名获取方式如下然后直接在双引号里面粘贴获取构造方法Declared是表示要获取所有类型的构造方法,不管是不是被public修饰getConstructors获取所有公共=......
  • Java笔记(数据结构与算法[树、栈、列表、队列、数组])
    Java笔记(数据结构与算法[树、栈、列表、队列、数组])链表栈,队列,数组树易错点:二叉树的插入,数据往二叉树里面插入的时候,每一个数据都要和每一个节点相比较,不可能插入到某两个节点中间,最后一定是挂(添加)到二叉树的最后一排的某个节点上度:每......
  • Java笔记(抽象类、接口、内部类、final关键字)
    Java笔记(抽象类、接口、内部类、final关键字)(一).抽象类抽象方法所在的类就是抽象类,抽象方法是在public和void中间加一个abstract,表示子类继承父类(父类是抽象类)的方法时必须重写,否则直接报错1.抽象方法和抽象类2.抽象类和抽象方法的定义格式3.抽象类和抽象方法的注意......
  • WPF笔记12——List<T> 与 ObservableCollection<T>
    代码1,使用ObservableCollection<T>:/*优点:(1)使用ObservableCollection<Student>来存储学生数据。在WPF中,ObservableCollection是一个非常适合数据绑定的集合类型。当集合中的元素发生变化(如添加、删除、修改元素)时,它会自动通知UI更新,这符合MVVM模式下视图与视图模型之间......
  • Vue2 使用wangEditor5 上传图片
    安装:yarnadd@wangeditor/editor#或者npminstall@wangeditor/editor--saveyarnadd@wangeditor/editor-for-vue#或者npminstall@wangeditor/editor-for-vue--save使用:模板<template><divstyle="border:1pxsolid#ccc;">......
  • 数据结构与算法学习笔记----Dijkstra
    数据结构与算法学习笔记----Dijkstra@@author:明月清了个风@@firstpublishtime:2024.12.17ps⭐️两个版本的都是模版题,算法讲解直接放在每一题里了,思路中还有对于稀疏图和稠密图的介绍,注意优化版的dijkstra中有几点注意点是和朴素版不一样的。Acwing849.Dijkstr......
  • 数据结构与算法学习笔记----SPFA算法
    数据结构与算法学习笔记----SPFA算法@@author:明月清了个风@@firstpublishtime:2024.12.19SPFA算法这同样是一种单源最短路径算法,是队列优化的Bellman-ford算法,因此他能处理的情况和Bellman-ford算法一样,但是在一般情况下,时间复杂度更低,为......
  • GO 学习笔记之三 基础语法(11) 数据类型转换
    一、将字符串类型的数字转换为数字类型1)使用 strconv 包中的 Atoi 函数Atoi 函数用于将字符串转换为int。如果字符串不是合法的int表示,函数会返回错误。packagemainimport("fmt""strconv")funcmain(){str:="123"num,err:=strco......
  • Cadence学习笔记 6-7 电源_LED电路原理图绘制
    基于Cadence17.4,四层板4路HDMI电路更多Cadence学习笔记:Cadence学习笔记1原理图库绘制Cadence学习笔记2PCB封装绘制Cadence学习笔记3MCU主控原理图绘制​Cadence学习笔记4单片机原理图绘制​Cadence学习笔记5四路HDMI原理图绘制目录 6、电源电路原理图绘制7......