商城day06

### 20. 用户-上传头像-持久层

**1. 分析SQL语句**

当用户上传头像时,服务器需要:

1. 存储用户上传的头像文件;

2. 将存储的文件的路径记录在数据库中。

对于持久层开发来说,需要做的就是将存储文件的路径更新到用户表中:

    UPDATE t_user 
    SET avatar=?, modified_user=?, modified_time=? 
    WHERE uid=?

**2. 接口与抽象方法**

在`UserMapper`接口中添加抽象方法:

    Integer updateAvatar(
        @Param("uid") Integer uid, 
        @Param("avatar") String avatar, 
        @Param("modifiedUser") String modifiedUser, 
        @Param("modifiedTime") Date modifiedTime);

**3. 配置映射**

配置的映射:

    <!-- 更新用户头像 -->
    <!-- Integer updateAvatar(
            @Param("uid") Integer uid, 
            @Param("avatar") String avatar, 
            @Param("modifiedUser") String modifiedUser, 
            @Param("modifiedTime") Date modifiedTime) -->
    <update id="updateAvatar">
        UPDATE 
            t_user 
        SET 
            avatar=#{avatar}, 
            modified_user=#{modifiedUser}, 
            modified_time=#{modifiedTime} 
        WHERE 
            uid=#{uid}
    </update>

单元测试:

    @Test
    public void updateAvatar() {
        Integer uid = 10;
        String avatar = "这里应该是头像的路径";
        String modifiedUser = "超级管理员";
        Date modifiedTime = new Date();
        Integer rows = mapper.updateAvatar(uid, avatar, modifiedUser, modifiedTime);
        System.err.println("rows=" + rows);
    }

### 21. 用户-上传头像-业务层

**1. 设计异常**

请参考:修改个人资料

**2. 接口与抽象方法**

请参考:修改个人资料

抽象方法的声明:

    /**
     * 更新个人头像
     * @param avatar 头像路径
     * @throws UserNotFoundException 用户数据不存在
     * @throws UpdateException 更新数据异常
     */
    void changeAvatar(Integer uid, String avatar) 
            throws UserNotFoundException, 
                UpdateException;

**3. 实现**

请参考:修改个人资料

先私有化实现持久层中新添加的方法:

    /**
     * 更新用户头像
     * @param uid 用户的id
     * @param avatar 头像的路径
     * @param modifiedUser 修改执行人
     * @param modifiedTime 修改时间
     */
    private void updateAvatar(
            Integer uid, String avatar, 
            String modifiedUser, Date modifiedTime) {
        Integer rows = userMapper.updateAvatar(uid, avatar, modifiedUser, modifiedTime);
        if (rows != 1) {
            throw new UpdateException(
                "修改用户数据时出现未知错误!");
        }
    }

实现的抽象方法:

    @Override
    public void changeAvatar(Integer uid, String avatar) throws UserNotFoundException, UpdateException {
        // 根据uid查询用户数据
        User result = findByUid(uid);
        // 判断查询结果是否为null
        if (result == null) {
            // 是:抛出UserNotFoundException
            throw new UserNotFoundException(
                "修改头像失败!尝试访问的用户不存在!");
        }

        // 判断查询结果中isDelete是否为1
        if (result.getIsDelete().equals(1)) {
            // 是:抛出UserNotFoundException
            throw new UserNotFoundException(
                "修改头像失败!尝试访问的用户不存在!");
        }
                        
        // 向user中封装modifiedUser和modifiedTime
        String modifiedUser = result.getUsername();
        Date modifiedTime = new Date();
        // 执行更新
        updateAvatar(uid, avatar, modifiedUser, modifiedTime);
    }

单元测试:

    @Test
    public void changeAvatar() {
        try {
            Integer uid = 10;
            String avatar = "新头像的路径";
            service.changeAvatar(uid, avatar);
            System.err.println("OK.");
        } catch (ServiceException e) {
            System.err.println(e.getClass().getName());
            System.err.println(e.getMessage());
        }
    }

### 22. 用户-上传头像-控制器层

**1. 处理异常**

创建上传时可能涉及的异常类:

    RuntimeException
    -- cn.tedu.store.controller.ex.FileUploadException
    -- -- cn.tedu.store.controller.ex.FileEmptyException
    -- -- cn.tedu.store.controller.ex.FileSizeException
    -- -- cn.tedu.store.controller.ex.FileContentTypeException
    -- -- cn.tedu.store.controller.ex.FileIllegalStateException
    -- -- cn.tedu.store.controller.ex.FileIOException

然后,需要在`BaseController`中添加对以上5种实际抛出的异常(不包括FileUploadException)的处理,在处理之前,应该修改原有的处理异常的方法之前的注解:

    @ExceptionHandler({ServiceException.class, FileUploadException.class})

然后,在处理异常的方法中添加5组else if语句,对这5种异常进行处理!

**2. 设计请求**

    请求路径:/users/change_avatar
    请求参数:HttpServletRequest request, MultipartFile file
    请求方式:POST
    响应数据:ResponseResult<String>
    是否拦截:是,无需修改配置

**3. 处理请求**

    // 确定上传文件的名称:UPLOAD_DIR
    private static final String UPLOAD_DIR = "upload";
    // 确定上传文件的最大大小
    private static final long UPLOAD_MAX_SIZE = 1 * 1024 * 1024;
    // 确定允许上传的类型的列表
    private static final List<String> UPLOAD_CONTENT_TYPES
        = new ArrayList<>();

    static {
        UPLOAD_CONTENT_TYPES.add("xxx");
        UPLOAD_CONTENT_TYPES.add("xxx");
        UPLOAD_CONTENT_TYPES.add("xxx");
        UPLOAD_CONTENT_TYPES.add("xxx");
    }

    @RequestMapping("/change_avatar")
    public ResponseResult<String> changeAvatar(
        HttpServletRequest request, 
        @RequestParam("file") MultipartFile file) {
        // 检查文件是否为空
        // 为空:抛出异常:FileEmptyException

        // 检查文件大小
        // 超出范围(> UPLOAD_MAX_SIZE):抛出异常:FileSizeException

        // 检查文件类型
        // 类型不符(contains()为false):抛出异常:FileContentTypeException

        // 确定文件夹路径:request.getServletContext().getRealPath(UPLOAD_DIR);
        // 创建上传文件夹的File对象parent
        // 检查文件夹是否存在,如果不存在,则创建

        // 获取原文件名:file.getOriginalFilename()
        // 从原文件名中得到扩展名
        // 确定文件名:uuid/nanoTime/...

        // 创建dest对象:new File(parent, filename);
        // try
        // 执行上传:file.transferTo(dest);
        // catch:IllegalStateException:抛出FileIllegalStateException
        // catch:IOException:抛出FileIOException

        // 获取uid:getUidFromSession(request.getSession());
        // 生成avatar:/UPLOAD_DIR/文件名.扩展名

        // 执行更新:userService.changeAvatar(uid, avatar);
        // 返回成功
    }

### 23. 用户-上传头像-界面


### 【附】 基于SpringMVC的文件上传【续】

**6. 关于上传的文件夹**

在处理请求的方法中添加参数`HttpServletRequest`,然后调用该参数对象的`getServletContext().getRealPath("upload")`即可获取到`webapp`下的名为`upload`的文件夹的实际路径,例如`D:\apache-tomcat-7.0.54\wtpwebapps\SPRINGMVC-03-UPLOAD\upload\`。

因为上传的文件大多后续将需要通过http方式被访问,所以,上传文件夹是应该在`webapp`之下!则应该通过以上方式获取文件夹的位置。

**7. 关于上传的文件名**

如果同一个项目中,上传的所有文件都将在同一个文件夹中,则需要保证每个文件的文件名是不相同的,否则,将会出现后续上传的文件会覆盖前序上传的文件!

如果要保证文件名是唯一的,可以使用UUID,也可以使用System.nanoTime(),或使用时间与随机数的组合等等多种方式!

通常,上传的文件的扩展名还是由原文件来决定,即上传过程中,不改变文件的扩展名,通过`MultipartFile`的`getOriginalFilename()`方法可以获取上传的文件的原名(该文件在客户端计算机中的原始名称),然后通过String类的API即可获取文件的扩展名:

    String originalFilename = file.getOriginalFilename();
    int beginIndex = originalFilename.lastIndexOf(".");
    String suffix = "";
    if (beginIndex > 0) {
        suffix = originalFilename.substring(beginIndex);
    }

**8. 关于MultipartFile接口的常用方法**

- `String getOriginalFilename()`:获取原文件名,即文件在客户端计算机中的名称;

- `boolean isEmpty()`:判断上传的文件是否为空,如果在上传表单中没有选中文件,或选中的文件没有内容(0字节),将视为空,则返回true,否则返回false;

- `long getSize()`:获取上传的文件的大小,以字节为单位,可以用于限制上传的文件的大小;

- `String getContentType()`:获取上传的文件的文档类型,其值是文件的MIME类型,可以用于限制上传的文件的类型;

- `InputStream getInputStream()`:获取上传的文件的输入流,主要用于自定义存储文件的过程,当自定义存储时,就不再需要调用`transferTo()`方法了;

- `void transferTo(File dest)`:将上传的文件存储为指定的文件。

**9. 关于MultipartResolver的配置**

在Spring的配置文件中,配置`CommonsMultipartResolver`时,可以为某些属性注入值,来完成某些配置:

- `maxUploadSize`:最大上传尺寸,以字节为单位,假设设置值为10M,则无论一次性上传多少个文件,其总和不允许超过10M;

- `maxUploadSizePerFile`:上传的每个文件的最大尺寸,以字节为单位,假设设置值是10M,如果一次上传多个文件,则每个文件的大小都不可以超过10M,但是,多个文件的总和是可以超过10M的;

- `defaultEncoding`:默认编码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值