FastDFS (五) --------- FastDFS 在 web 项目中的应用


前言

本篇文章我们主要介绍 FastDFS 在 web 项目中的应用,通过完成 一个 P2P 项目合同管理的例子,在 WEB 项目中实现对文件的上传、下载和删除操作。

我们做这个项目的主要目标是 :

  • 实现对 pdf 文件上传、下载、删除
  • 熟练使用 Springboot + thymeleaf

一、数据库环境搭建

A、创建数据库 fastdfs

在这里插入图片描述
B、在该库下创建 creditor_info 表

CREATE TABLE `creditor_info` (
	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
	`realName` varchar(35) DEFAULT NULL COMMENT '债权借款人姓名',
	`idCard` varchar(18) DEFAULT NULL COMMENT '债权借款人身份证',
	`address` varchar(150) DEFAULT NULL COMMENT '债权借款人地址',
	`gender` int(1) DEFAULT NULL COMMENT '1男2女',
	`phone` varchar(11) DEFAULT NULL COMMENT '债权借款人电话',
	`money` decimal(10,2) DEFAULT NULL COMMENT '债权借款人借款金额',
	`groupName` varchar(10) DEFAULT NULL COMMENT '债权合同所在组',
	`remoteFilePath` varchar(150) DEFAULT NULL COMMENT '债权合同所在路径',
	 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

在这里插入图片描述

二、开发环境搭建

A、创建 SpringBoot 项目 fastdfs-web,添加 Web 和 Thymeleaf 依赖

在这里插入图片描述

在这里插入图片描述
B、在 pom.xml 文件中添加 Mybatis 依赖及 MySQL 依赖

!-- 加载mybatis整合springboot -->
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <!--在springboot的父工程中没有指定版本,我们需要手动指定-->
   <version>1.3.2</version>
</dependency>
<!-- MySQL的jdbc驱动包 -->
<dependency>
   <groupId>mysql</groupId>
   <!--在springboot的父工程中指定了版本,我们就不需要手动指定了-->
   <artifactId>mysql-connector-java</artifactId>
</dependency>

C、在pom.xml文件中添加resources,指定编译的位置

<resources>
   <resource>
      <directory>src/main/java</directory>
      <includes>
         <include>**/*.xml</include>
      </includes>
   </resource>
   <resource>
      <directory>src/main/resources</directory>
      <includes>
         <include>**/*.*</include>
      </includes>
   </resource>
   <!--如果存在jsp,需要指定jsp文件编译的位置-->
</resources>

D、在 SpringBoot 主配置文件 application.properties 中添加数据库配置信息

#数据库的连接配置信息
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.160.133:3306/fastdfs?useUnicode=true&characterEncoding=utf8&useSSL=false

E、创建相关的包和类

创建 controller,model,mapper,service 包,及其子包 impl。

创建 CreditorInfoController 类、CreditorInfoService 接口、创建 CreditorInfoServiceImpl 实现类。

在这里插入图片描述

F、实体类

package com.fancy.fastdfsweb.mapper;

public class CreditorInfo {
    private Integer id;
    private String realName;
    private String idCart;
    private String address;
    private Integer gender;
    private String phone;
    private Double money;
    private String groupName;
    private String remoteFilePath;

    public CreditorInfo() {
    }

    public CreditorInfo(Integer id, String realName, String idCart, String address, Integer gender, String phone, Double money, String groupName, String remoteFilePath) {
        this.id = id;
        this.realName = realName;
        this.idCart = idCart;
        this.address = address;
        this.gender = gender;
        this.phone = phone;
        this.money = money;
        this.groupName = groupName;
        this.remoteFilePath = remoteFilePath;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public String getIdCart() {
        return idCart;
    }

    public void setIdCart(String idCart) {
        this.idCart = idCart;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getRemoteFilePath() {
        return remoteFilePath;
    }

    public void setRemoteFilePath(String remoteFilePath) {
        this.remoteFilePath = remoteFilePath;
    }
}

三、功能设计

1. 展示所有债权信息

A、在 CreditorInfoController 类中创建 index 方法,将 CreditorInfoService 注入到 controller 中

@Autowired
private CreditorInfoService creditorInfoService;

@GetMapping("/fastdfs/index")
public String index(Model model) {
    List<CreditorInfo> creditorInfoList = creditorInfoService.getAllCreditorInfo();
    model.addAttribute("creditorInfoList", creditorInfoList);
    //模板页面, 不是 jsp
    return "index";
}

B、在 CreditorInfoService 中提供 getAllCreditorInfo 方法

package com.fancy.fastdfsweb.service;

import com.fancy.fastdfsweb.model.CreditorInfo;

import java.util.List;

public interface CreditorInfoService {
    List<CreditorInfo> getAllCreditorInfo();
}

C、在 CreditorInfoServiceImpl 中对 getAllCreditorInfo 方法进行实现

package com.fancy.fastdfsweb.service.impl;

import com.fancy.fastdfsweb.model.CreditorInfo;
import com.fancy.fastdfsweb.service.CreditorInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CreditorInfoServiceImpl implements CreditorInfoService {
    
    @Autowired
    private CreditorMapper creditorInfoMapper;

    @Override
    public List<CreditorInfo> getAllCreditorInfo() {
        return creditorInfoMapper.selectAllCreditorInfo();
    }
}

D、在 CreditorMapper 接口中定义 selectAllCreditorInfo 方法

package com.fancy.fastdfsweb.mapper;

import com.fancy.fastdfsweb.model.CreditorInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface CreditorMapper {
     List<CreditorInfo> selectAllCreditorInfo();
}

E、在 IDEA 中安装 free Mybatis 插件

插件可以通过点击 Mapper 接口中的方法,进入到 .xml 文件

在这里插入图片描述
F、定义 mapper 映射文件相关 SQL 语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fancy.fastdfsweb.mapper.CreditorMapper">
    <select id="selectAllCreditorInfo" resultType="com.fancy.fastdfsweb.model.CreditorInfo">
        select * from  creditor_info;
    </select>
</mapper>

G、展示页面的设计

在项目的 templates 目录下创建 index.html,初步展示出数据库中数据

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>债权合同管理</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    <script th:src="@{/js/jquery-3.6.0.min.js}"></script>
    <script th:src="@{/js/bootstrap.min.js}"></script>
</head>
<body style="margin: 50px">
	<table class="table table-striped">
	    <caption>债权合同信息列表</caption>
	    <thead>
	    <tr>
	        <th>序号</th>
	        <th>债权借款人姓名</th>
	        <th>债权借款人身份证</th>
	        <th>债权借款人住址</th>
	        <th>债权借款人手机号</th>
	        <th>债权借款人性别</th>
	        <th>债权借款人借款金额</th>
	    </tr>
	    </thead>
	    <tbody>
	    <tr th:each="creditorInfo:${creditorInfoList}">
	        <td th:text="${creditorInfoStat.count}"></td>
	        <td th:text="${creditorInfo.realName}"></td>
	        <td th:text="${creditorInfo.idCard}"></td>
	        <td th:text="${creditorInfo.address}"></td>
	        <td th:text="${creditorInfo.phone}"></td>
	        <td th:text="${creditorInfo.gender == 1 ?'':''}"></td>
	        <td th:text="${creditorInfo.money}"></td>
	    </tr>
	    </tbody>
	</table>
</body>
</html>

<html lang="en" xmlns:th="http://www.thymeleaf.org"> 在 html 标签上加上 Thymeleaf 的命名空间

H、向数据库中加几条数据

在这里插入图片描述
I、启动项目,访问 http://localhost:8080/fastdfs/index 查看效果

在这里插入图片描述

2. 为某一个债权合同上传文件

A、在 index.html 中添加操作列

<th>合同管理</th>
 <td>
    <a th:href="@{'/fastdfs/toUpload?id=' + ${creditorInfo.id}}">上传</a>
 </td>

B、在 CreditorController 中添加跳转到上传页面的方法 toUpload

@GetMapping("/fastdfs/toUpload")
public String toUpload(Model model, @RequestParam("id") Integer id) {
    model.addAttribute("id", id);
    return "upload";
}

C、在 templates 下创建 upload.html 页面

!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>债权合同上传</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    <script th:src="@{/js/jquery-3.6.0.min.js}"></script>
    <script th:src="@{/js/bootstrap.min.js}"></script>
</head>
<body>
    <form th:action="@{/fastdfs/upload}" class="form-inline" role="form" method="post" enctype="multipart/form-data">
        <div class="form-group">
            <label class="sr-only" for="fileName">文件输入</label>
            <input type="file" id="fileName" name="fileName">
        </div>
        <input type="hidden" name="id" th:value="${id}">
        <button type="submit" class="btn btn-default">提交</button>
    </form>
</body>
</html>

☹ 文件上传必须是 post 请求
☹ enctype 必须为 multipart/form-data, 该属性规定在发送到服务器之前应该如何对表单数据进行编码。
☹ 合同的 id 通过隐藏域传递

D、在 pom.xml 文件中加入 FastDFS 客户端的 jar 包依赖

<dependency>
	<groupId>net.oschina.zcx7878</groupId>
	<artifactId>fastdfs-client-java</artifactId>
	<version>1.27.0.0</version>
</dependency>

E、将 FastDFS 客户端的配置文件 fast_client.conf 拷贝到 resources 目录下

在这里插入图片描述

F、将原来我们封装的 FastDFS 类拷贝到 fastdfs 包下,修改其中的 file_upload 方法,定义一些参数

public static String[] fileUpload(byte[] fileBytes, String fileExt){
    String[] uploadArray = null;
    try {
        //1. 获取StorageClient对象
        StorageClient storageClient = getStorageClient();
        //2.上传文件  第一个参数:本地文件路径 第二个参数:上传文件的后缀 第三个参数:文件信息
        uploadArray = storageClient.upload_file(fileBytes,fileExt,null);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (MyException e) {
        e.printStackTrace();
    } finally {
        closeFastDFS();
    }
    return uploadArray;
}

G、在 CreditorController 中添加处理上传文件的方法

@PostMapping("/fastdfs/upload")
@ResponseBody
public String upload(@RequestParam("id") Integer id, @RequestParam("fileName")MultipartFile file) {
    // 原来文件上传是将文件写到本地或者远程服务器的某个目录下
    // 现在的文件上传是将文件上传到 fastdfs 文件服务器上
    // 1表示上传失败  0表示成功
    int result = 1;
    //abc.txt -->txt
    String fileExt = file.getOriginalFilename().substring(file.getOriginalFilename().indexOf(".") + 1);
    try {
        String[] uploadArray = FastDFSUtil.fileUpload(file.getBytes(), fileExt);
        if (uploadArray != null && uploadArray.length == 2) {
            // 文件上传到fastDFS成功  ,将合同文件路径更新到债权记录中
            CreditorInfo creditorInfo = new CreditorInfo();
            creditorInfo.setId(id);
            creditorInfo.setGroupName(uploadArray[0]);
            creditorInfo.setRemoteFilePath(uploadArray[1]);
            int updateRow = creditorInfoService.updateCreditorInfo(creditorInfo);
            if (updateRow > 0) {
                result = 0;
            }
            
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
    return "<script>window.parent.uploadOK('" + result + ")</script>";
}

H、在 CreditorInfoService 中添加 updateCreditorInfo 方法

int updateCreditorInfo(CreditorInfo creditorInfo);

I、在 CreditorInfoServiceImpl 中添加 updateCreditorInfo 方法实现

@Override
public int updateCreditorInfo(CreditorInfo creditorInfo) {
    return creditorInfoMapper.updateCreditorInfo(creditorInfo);
}

J、在 CreditorMapper 中定义方法 updateCreditorInfoById

int updateCreditorInfo(CreditorInfo creditorInfo);

K、定义 mapper 中插入语句

<update id="updateCreditorInfo" parameterType="com.fancy.fastdfsweb.model.CreditorInfo">
    update  creditor_info set groupName = #{groupName} , remoteFilePath = #{remoteFilePath} where id = #{id};
</update>

L、在 upload.html 做一个类似 ajax 的页面不刷新效果

在 upload.html 页面中加一个 iframe。
将 upload.html 页面中的 form 中的 target 设置为 iframe 的 name。
在 iframe 的父页面中,写一个函数,处理上传结果。

 <iframe name="uploadFrame" style="display: none;"></iframe>
 <script type="text/javascript" th:inline="javascript">
     function uploadOK(result){
         if(result == 0){
             //文件上传成功
             alert("文件上传成功");
             var contextPath = [[${#request.getContextPath()}]];
             window.location.href = contextPath + "/fastdfs/index";
         }else{
             alert("文件上传失败");
         }
     }
 </script>

M、如果上传文件超出了 1M,需要在 application.properties 中配置 SpringBoot 上传文件的最大限制

在这里插入图片描述

3. 下载某一个债权合同

A、修改 index.html 页面,下载加连接,并做判断

<span th:if="${creditorInfo.getGroupName() ne null && creditorInfo.getRemoteFilePath() ne null }">
   <a th:href="@{'/fastdfs/download?id=' + ${creditorInfo.id}}">下载</a>
</span>

B、在 CreditorController 中,完成下载的请求

☹ ResponseEntity 通常用于返回文件流。
☹ @ResponseBody 可以直接返回Json结果。
☹ ResponseEntity 不仅可以返回 json 结果,还可以定义返回的 HttpHeaders 和 HttpStatus。
☹ ResponseEntity 的优先级高于 @ResponseBody,在不是 @ResponseEntity 的情况下才去检查有没有 @ResponseBody 注解。如果响应类型是ResponseEntity 可以不写 @ResponseBody 注解,写了也没有关系。

@GetMapping("/fastdfs/download")
public ResponseEntity<byte[]> download(@RequestParam("id") Integer id) {
    CreditorInfo creditorInfo = creditorInfoService.getAllCreditorInfo(id);
    String extName = creditorInfo.getRemoteFilePath().substring(creditorInfo.getRemoteFilePath().indexOf("."));
    byte[] fileBytes = FastDFSUtil.fileDownload(creditorInfo.getGroupName(), creditorInfo.getRemoteFilePath());
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);//流类型
    httpHeaders.setContentDispositionFormData("attachment", System.currentTimeMillis() + extName);
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(fileBytes, httpHeaders, HttpStatus.OK);
    return responseEntity;
}

C、在 CreditorService 接口中添加 getCreditorInfoById 的方法

CreditorInfo getCreditorInfoById(Integer id);

D、在 CreditorServiceImpl 中添加 getCreditorInfoById 方法的实现

@Override
public CreditorInfo getCreditorInfoById(Integer id) {
    return creditorInfoMapper.selectCreditorInfoById();
}

E、定义 mapper 类及其配置文件 selectCreditorInfoById 方法

在这里插入图片描述
在这里插入图片描述
F、修改 FastDFS 类中 fileDown 方法的实现,传递参数

public static byte[] fileDownload(String groupName, String remoteFilePath){
    byte[] fileBytes = null;
    try {
        //1. 获取StorageClient对象
        StorageClient storageClient = getStorageClient();
        //2.下载文件 返回0表示成功,其它均表示失败
        fileBytes = storageClient.download_file(groupName, remoteFilePath);
        
    } catch (IOException e) {
        e.printStackTrace();
    } catch (MyException e) {
        e.printStackTrace();
    } finally {
        closeFastDFS();
    }
    return  fileBytes;
}

4. 删除某一个债权合同,使用 ajax 实现异步删除

A、在 index.html 页面为删除加超链接

<a th:href="@{'javascript:deleteFile(' + ${creditorInfo.id} + ')'}">删除</a>

此超链接与删除放在同一个span中

B、index.html 页面提供 js 方法,并发送 ajax 请求,对响应结果进行处理

<script type="text/javascript" th:inline="javascript">
    function deleteFile(id) {
        var contextPath = [[${#request.getContextPath()}]];
        $.ajax({
            url:contextPath + "/fastdfs/fileDelete",
            type:"post",
            data:{
                "id" : id
            },
            success:function(responseMsg) {
                if (responseMsg == 0) {
                    alert("删除成功");
                    window.location.reload();
                } else {
                    alert("删除失败");
                }
            }
            
        });
    }
</script>

C、在 CreditorController 中处理删除请求

注意:删除 FastDFS 和清除数据库,所以我们将这些业务都放在 service 中进行事务的处理

@RequestMapping("/fastdfs/fileDelete") 
@ResponseBody
public  String fileDelete(@RequestParam("id") Integer id) {
    int result = 1;
    try {
        result = creditorInfoService.deleteContract(id);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return String.valueOf(result);
}

D、在 CreditorService 接口中加删除合同的方法 deleteContract

因为目前提供的方法,如果 group 和 remoteFilePath 为空就不更新,所以我们需要自己提供。

在这里插入图片描述
E、在 CreditorServiceImpl 类中对 deleteContract 方法进行实现

@Override
@Transactional //加上该注解控制事务
public int deleteContract(Integer id) {
    int result = 1;
    //根据债权id获取债权信息
    CreditorInfo creditorInfo = creditorInfoMapper.selectCreditorInfoById(id);
    //注意:事务控制的数据库,所以我们先对数据库进行更新, 在操作FastDFS, 如果操作FastDFS失败了,那么对数据库的操作回滚
    int updateRow = creditorInfoMapper.updateContractById(id);
    if (updateRow > 0) {
        // 如果数据库更新, 那么删除 FastDFS 的文件
        if (updateRow > 0) {
            int num = FastDFSUtil.fileDelete(creditorInfo.getGroupName(), creditorInfo.getRemoteFilePath());
            if (num == 0) {
                result = 0;
            } else {
                throw new RuntimeException("FastDFS 文件删除失败");
            }
        }
    }
    return result;
}

F、在 CreditorMapper 类中添加更新的方法

int updateContractById(Integer id);

G、在 CreditorMapper.xml 中添加更新的方法

<update id="updateContractById" parameterType="java.lang.Integer">
    update creditor_info set  groupName = NULL, remoteFilePath = NULL where id = #{id, jdbcType=INTEGER}
</update>

H、修改 FastDFS 类中的 fileDelete 方法,提供参数

public static int fileDelete(String groupName, String remoteFilePath){
    int num = 1;
    try {
        //1. 获取StorageClient对象
        StorageClient storageClient = getStorageClient();
        //2.删除文件 返回0表示成功,其它均表示失败
        num = storageClient.delete_file(groupName, remoteFilePath);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (MyException e) {
        e.printStackTrace();
    } finally {
        closeFastDFS();
    }
    return num;
}

I、在 Application 类上开启事务支持
在这里插入图片描述

然后在浏览器中进行测试即可

如果想要美化弹窗,推荐使用 弹层组件 layer,官网 :https://www.layui.com/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在森林中麋了鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值