文件存储

JAVAfile

# 一、文件存储方式

  • 本地目录存储
  • 数据库存储
  • 自建文件服务器存储
  • 云存储服务器

# 二、数据库存储

# 限制性

  • 文件资源小,资源过多该方式则不适用
  • 上传频率低,基本等同于获取静态资源

# 概念

# BLOB

1.BLOB

BLOB全称为二进制大型对象(Binary Large Object)。它用于存储数据库中的大型二进制对象。可存储的最大大小为4G字节。

通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。

MySQL有四种BLOB类型:

  • tinyblob:仅255个字符

  • blob:最大限制到65K字节

  • mediumblob:限制到16M字节

  • longblob:可达4GB

2.CLOB

CLOB全称为字符大型对象(Character Large Object)。它与LONG数据类型类似,只不过CLOB用于存储数据库中的大型单字节字符数据块,不支持宽度不等的字符集。可存储的最大大小为4G字节。

# BASE64

图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址。

图片的 base64字符串如下:

data:image/jpeg;base64,BASE64编码
1

为什么使用图片的 base64 编码

  • 无额外请求
  • 对于极小或者极简单图片
  • 可像单独图片一样使用,比如背景图片重复使用等
  • 没有跨域问题,无需考虑缓存、文件头或者cookies问题

我们所看到的网页上的每一个图片,都是需要消耗一个 http 请求下载而来的。

而使用图片的 base64 编码,则可以通过在获取其他数据的同时,把图片数据作为一个字符串进行同步返回。

那么前端页面显示如下:

css中的写法:

#mycss {
  background: url(BASE64字符串) no-repeat center;
}
1
2
3

html标签中的写法:

<img src="BASE64字符串">
1

# 存储流程

# 后端转换

  1. 上传文件流到后端服务
  2. 将文件流转为byte[]存入数据库

后端接口接收:

@PostMapping(value = "/uploadImage", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseResult uploadImage(@RequestParam MultipartFile file) {
    try {
        Long fileId = fileService.upload2DB(file);
        return ResponseResult.success("上传文件成功", fileId);
    } catch (Exception e) {
        logger.error("【捕获异常-上传文件操作】\r\n异常记录:", e);
        throw new BusinessException(e.getMessage());
    }
}
1
2
3
4
5
6
7
8
9
10

入库处理:


@Autowired
private SysFileMapper sysFileMapper;

public Long upload2DB(MultipartFile file) throws IOException {
    if (file.isEmpty()) {
        throw new BusinessException(ResponseCode.UPLOAD_FILE_NULL);
    }
    InputStream inputStream = file.getInputStream();
    byte[] imageByte = new byte[(int) file.getSize()];
    inputStream.read(imageByte);

    // 文件名
    String fileName = file.getOriginalFilename();
    // 后缀名
    String suffixName = fileName.substring(fileName.lastIndexOf("."));

    SysFile sysFile = new SysFile();
    sysFile.setFileName(fileName);
    sysFile.setFileContent(imageByte);
    sysFile.setFileSize(file.getSize());
    sysFile.setFileExt(suffixName);
    sysFile.setFileType(2);
    sysFileMapper.insert(sysFile);

    return sysFile.getId();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

实体类:

@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("sys_file")
public class SysFile extends BaseDO {

    private static final long serialVersionUID = 3094398542939760859L;

    /**
    * 文件名
    */
    private String fileName;

    /**
    * 文件内容
    */
    private String filePath;

    /**
    * 文件内容
    */
    private byte[] fileContent;

    /**
    * 文件大小:单位(kb)
    */
    private Long fileSize;

    /**
    * 文件扩展名
    */
    private String fileExt;

    /**
    * 文件类型:1、Doc;2、Img;3、Video;9、Other
    */
    private Integer fileType;

    /**
    * 备注说明
    */
    private String memo;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

数据库表结构:

DROP TABLE IF EXISTS `sys_file`;
CREATE TABLE `sys_file`  (
  `id` bigint(0) NOT NULL COMMENT '文件主键ID',
  `file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件名',
  `file_path` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '文件路径',
  `file_content` mediumblob COMMENT '文件内容',
  `file_size` int(0) NOT NULL COMMENT '文件大小:单位(字节)',
  `file_ext` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '文件扩展名',
  `file_type` tinyint(0) DEFAULT NULL COMMENT '文件类型:1、Doc;2、Img;3、Video;9、Other',
  `memo` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注说明',
  `create_time` datetime(0) NOT NULL COMMENT '创建时间',
  `update_time` datetime(0) DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文件信息表' ROW_FORMAT = Dynamic;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 前端转换

  1. 在前端将文件转换为base64编码字符串
  2. 后端服务直接入库存储base64编码字符串

# 展示流程

  1. 上读取数据库blob数据
  2. 将byte[]文件流转换为base64

读取数据库blob数据

@Override
public String getImage(Long fileId) {
    return FileUtils.getImageBase64(getBytes(fileId));
}

private byte[] getBytes(Long fileId) {
    if (fileId == null) {
        throw new BusinessException(ResponseCode.PARAM_IS_BLANK);
    }
    SysFile sysFile = sysFileMapper.selectById(fileId);
    if (sysFile.getFileContent() == null) {
        logger.error("文件主键ID为{}", fileId);
        throw new BusinessException(ResponseCode.FILE_IS_NOT_EXIST);
    }
    return sysFile.getFileContent();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

获取base64编码工具类

public static final String IMAGE_BASE_PREFIX = "data:image/png;base64,";

public static String getImageBase64(byte[] fileBytes) {
    return IMAGE_BASE_PREFIX + bytesToBase64(fileBytes);
}

public static String bytesToBase64(byte[] fileBytes) {
    return Base64.encodeBase64String(fileBytes);
}
1
2
3
4
5
6
7
8
9

前端标签展示

html标签中的写法:

<img src="BASE64字符串">
1

# 三、本地目录存储

# 本地存储缺点

  1. 项目打包困难,当上传的文件越来越多,项目的打包jar越来越大。
  2. 代码与文件数据不能分开存储,就意味着文件数据的备份将变得复杂.

# 四、远程文件服务器存储

  1. 自建分布式文件服务器fastDFS
  2. 自己分布式文件服务器MinIO
  3. 购买阿里云OSS云存储服务器;