# GridFs 操作文件

# GridFs 介绍

GridFs 是 MongoDB 提供的用于持久化存储文件的模块,CMS 使用 MongoDB 存储数据,使用 GridFs 可以快速集成开发

它的工作原理是:

在 GridFs 存储文件时将文件分块存储,文件会按照 256KB 的大小分割成多个块进行存储,GridFs 使用两个集合 (collection) 存储文件如下图

image-20240910223644811

一个集合是 chunks (分块),用于存储文件的二进制数据;另一个集合是 files (完整文件),用于存储文件的元数据信息 (文件名称,块大小,上传时间等信息)

从 GridFs 中读取文件要对文件的各个块进行组装,合并。

# 引入 pom 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.poi-learn</groupId>
    <artifactId>maven-mybatis-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>maven-mybatis-test</name>
    <description>maven-mybatis-test</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

实际引入 mongodb 的即可,这个项目是我 玩 mybatis 导入的其他依赖

配置 mongodb 数据库配置到 yml

server:
  port: 80
  servlet:
    context-path: /dkx
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useUnicode=true
    username: root
    password: dkx..
  main:
    banner-mode: off
  data:
    mongodb:
      uri: mongodb://localhost:27017/test
      database: test
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
  global-config:
    banner: off

实际配置 mongodb 的配置即可

如果要对一个集合进行操作那么需要对实体类进行配置相关的注解如下:

  • @Document (collection = "shop") 对当前实体类声明集合名称
  • @Id 让 mongodb 在插入一条数据的时候自动生成_id 的值
package com.poilearn.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * <p>
 * 商品表
 * </p>
 *
 * @author dkx
 * @since 2024-09-08
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@TableName("tab_shop")
@Document(collection = "shop")
public class Shop implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    @Id
    private String id;
    /**
     * 商品名称
     */
    @TableField("name")
    private String name;
    /**
     * 商品描述
     */
    @TableField("description")
    private String description;
    /**
     * 商品类型
     */
    @TableField("type")
    private String type;
    /**
     * 商品价格
     */
    @TableField("price")
    private BigDecimal price;
}

# 存储文件

controller

@RestController
@RequestMapping("/poilearn/shop")
public class ShopController {
    @Autowired
    private IShopService iShopService;
  	 @PostMapping("/addFile")
    public String addFile(@RequestBody MultipartFile file) {
        return iShopService.addPNG(file);
    }
}

serviceImpl

@Service
@Slf4j
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private CsdnUserInfoMapper csdnUserInfoMapper;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
   public String addPNG(MultipartFile file) {
      try {
         InputStream inputStream = file.getInputStream();
         return gridFsTemplate.store(inputStream, file.getOriginalFilename(), file.getContentType()) + "";
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }
}

测试结果:

image-20240911082615164

image-20240911082646886

# 下载文件

# 在 config 包中定义 Mongodb 的配置类

如下:

GridFsBucket 用于打开下载流对象

config

package com.example.demomonogo.config;
/**
 * @author john
 * @date 2019/12/21 - 10:39
 */
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MongoConfig {
    @Value("${spring.data.mongodb.database}")
    String db;
    @Bean
    public GridFSBucket getGridFSBucket(MongoClient mongoClient) {
        MongoDatabase database = mongoClient.getDatabase(db);
        GridFSBucket bucket = GridFSBuckets.create(database);
        return bucket;
    }
}

controller

@RestController
@RequestMapping("/poilearn/shop")
public class ShopController {
    @Autowired
    private IShopService iShopService;
	@GetMapping("/downFile")
    public void downFile(@RequestParam("id") String id) {
        iShopService.downloadPng(id);
    }
}

serviceImpl

@Service
@Slf4j
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private CsdnUserInfoMapper csdnUserInfoMapper;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
   
	public void downloadPng(String id) {
        GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)));
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
        GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
        File file = new File("D:\\png\\", "test.png");
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        try (
                InputStream inputStream = gridFsResource.getInputStream();
                FileOutputStream fileOutputStream = new FileOutputStream(file)
        ) {
            byte[] bytes = new byte[1024];
            int i = 0;
            while ((i = inputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, i);
            }
            fileOutputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

测试结果:

拿着刚才存储文件的_id 请求下载下这个文件

image-20240911083116288

image-20240911083137342

# 删除文件

通过_id 来删除某个文件或者直接全部删除文件的时候 将会连带该文件的分块也一并被删除掉

controller

@RestController
@RequestMapping("/poilearn/shop")
public class ShopController {
    @Autowired
    private IShopService iShopService;
   @GetMapping("/delFile")
    public void delFile(@RequestParam("id") String id) {
        iShopService.delPng(id);
    }
}

serviceImpl

@Service
@Slf4j
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private CsdnUserInfoMapper csdnUserInfoMapper;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
   public void delPng(String id) {
        // 会同时删除分块数据
        gridFsTemplate.delete(Query.query(Criteria.where("_id").is(id)));
    }
}

测试结果

image-20240911083440929

image-20240911083501097