三明数字档案馆系统核心文件处理引擎开发实战
一、开发环境与基础容器搭建
为了确保三明数字档案馆系统的核心文件处理引擎能够稳定运行,我们将采用Docker容器化技术来快速部署基础依赖环境。请确保服务器已安装Docker及Docker Compose,版本分别不低于20.10和2.0。
在项目根目录下创建docker-compose.yml文件,直接复制以下内容。该配置将初始化MySQL 8.0数据库、MinIO对象存储服务以及Redis缓存服务,这些是档案系统存储和索引的核心组件。
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: sm_archive_mysql
environment:
MYSQL_ROOT_PASSWORD: SmArchive@2024Root
MYSQL_DATABASE: sm_archive_db
MYSQL_USER: archive_user
MYSQL_PASSWORD: Archive@Pass123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
minio:
image: minio/minio:latest
container_name: sm_archive_minio
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: Minio@Admin2024
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
redis:
image: redis:7-alpine
container_name: sm_archive_redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
volumes:
mysql_data:
minio_data:
redis_data:
保存文件后,在终端执行docker-compose up -d命令启动所有服务。启动成功后,需在浏览器访问http://服务器IP:9001登录MinIO控制台,使用上述配置中的账号密码创建一个名为sm-archive-bucket的Bucket,并设置访问权限为Public,以便后续文件预览功能的实现。
二、数据库表结构设计
进入MySQL容器初始化档案核心表。执行以下命令进入数据库命令行:
docker exec -it sm_archive_mysql mysql -uarchive_user -pArchive@Pass123 sm_archive_db
在MySQL命令行中直接执行以下SQL脚本,建立档案元数据表sm_archive_metadata。该表设计了文件哈希校验字段,用于防止重复归档,这是数字档案馆建设中的关键需求。
CREATE TABLE sm_archive_metadata (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
archive_code VARCHAR(64) NOT NULL COMMENT '档案编号,唯一标识',
title VARCHAR(255) NOT NULL COMMENT '档案标题',
file_name VARCHAR(255) NOT NULL COMMENT '原始文件名',
file_path VARCHAR(512) NOT NULL COMMENT 'MinIO存储路径',
file_size BIGINT NOT NULL COMMENT '文件大小(字节)',
file_hash VARCHAR(64) NOT NULL COMMENT 'SHA-256文件哈希,用于查重',
mime_type VARCHAR(100) COMMENT '文件MIME类型',
ocr_content TEXT COMMENT 'OCR识别后的全文内容',
status TINYINT DEFAULT 0 COMMENT '状态:0-待处理,1-已归档,2-OCR处理中',
created_by VARCHAR(50) DEFAULT 'system' COMMENT '创建人',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY uk_archive_code (archive_code),
KEY idx_file_hash (file_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='三明数字档案馆档案元数据表';
三、后端核心服务实现
本指南使用Spring Boot 3.2作为后端框架。首先创建Maven项目,在pom.xml中引入必要的依赖。请确保版本号准确,避免依赖冲突。
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
8.0.33
io.minio
minio
8.5.7
org.apache.tika
tika-core
2.9.1
org.projectlombok
lombok
true
在src/main/resources/application.yml中配置数据库连接及MinIO参数。请勿修改端口号,除非你的服务器端口已被占用。

server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/sm_archive_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: archive_user
password: Archive@Pass123
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none
show-sql: true
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
secretKey: Minio@Admin2024
bucketName: sm-archive-bucket
创建核心实体类ArchiveMetadata.java,对应数据库表结构。
package com.sm.archive.entity;
import jakarta.persistence.;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "sm_archive_metadata")
public class ArchiveMetadata {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 64)
private String archiveCode;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String fileName;
@Column(nullable = false)
private String filePath;
private Long fileSize;
@Column(name = "file_hash", nullable = false, length = 64)
private String fileHash;
private String mimeType;
@Column(columnDefinition = "TEXT")
private String ocrContent;
private Integer status;
private String createdBy;
@Column(name = "create_time")
private LocalDateTime createTime;
}
编写文件上传与处理的Service层ArchiveService.java。这里实现了文件上传到MinIO、计算SHA-256哈希以及调用Tika进行简单的元数据提取。
package com.sm.archive.service;
import com.sm.archive.entity.ArchiveMetadata;
import com.sm.archive.repository.ArchiveRepository;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.apache.tika.Tika;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.util.HexFormat;
@Service
public class ArchiveService {
private final MinioClient minioClient;
private final ArchiveRepository repository;
private final Tika tika = new Tika();
@Value("${minio.bucketName}")
private String bucketName;
public ArchiveService(MinioClient minioClient, ArchiveRepository repository) {
this.minioClient = minioClient;
this.repository = repository;
}
public String uploadAndArchive(MultipartFile file, String title) throws Exception {
// 1. 计算文件哈希
String fileHash = calculateHash(file.getInputStream());
// 2. 检查重复
if (repository.findByFileHash(fileHash).isPresent()) {
throw new RuntimeException("档案文件已存在,请勿重复归档");
}
// 3. 生成存储路径
String fileName = file.getOriginalFilename();
String objectName = "archives/" + System.currentTimeMillis() + "_" + fileName;
// 4. 上传到MinIO
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
// 5. 提取元数据
String mimeType = tika.detect(file.getInputStream());
// 6. 保存数据库记录
ArchiveMetadata metadata = new ArchiveMetadata();
metadata.setArchiveCode(generateArchiveCode());
metadata.setTitle(title);
metadata.setFileName(fileName);
metadata.setFilePath(objectName);
metadata.setFileSize(file.getSize());
metadata.setFileHash(fileHash);
metadata.setMimeType(mimeType);
metadata.setStatus(1); // 已归档
metadata.setCreateTime(LocalDateTime.now());
repository.save(metadata);
return "归档成功,档案编号: " + metadata.getArchiveCode();
}
private String calculateHash(InputStream is) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] buffer = new byte[8192];
int read;
while ((read = is.read(buffer)) != -1) {
digest.update(buffer, 0, read);
}
return HexFormat.of().formatHex(digest.digest());
}
private String generateArchiveCode() {
return "SM-" + System.currentTimeMillis();
}
}
创建Controller层ArchiveController.java提供REST接口。
package com.sm.archive.controller;
import com.sm.archive.service.ArchiveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/archive")
public class ArchiveController {
@Autowired
private ArchiveService archiveService;
@PostMapping("/upload")
public ResponseEntity uploadFile(
@RequestParam("file") MultipartFile file,
@RequestParam("title") String title) {
try {
String result = archiveService.uploadAndArchive(file, title);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.internalServerError().body("归档失败: " + e.getMessage());
}
}
}
四、前端上传交互实现
使用Vue 3和Element Plus构建前端上传页面。首先安装依赖:npm install element-plus axios。在App.vue中编写以下代码,实现文件选择、拖拽上传及进度显示。
拖拽文件到此处或 点击上传
确认归档
五、功能验证与测试
完成上述代码编写后,启动Spring Boot后端服务。在终端进入项目目录执行:
mvn clean install
mvn spring-boot:run
待服务启动完毕,启动Vue前端服务:
npm run dev
打开浏览器访问前端地址,输入标题“三明市2023年度政务公开文件”,选择一个测试用的PDF或Word文档进行上传。观察控制台日志,确认以下步骤无误:
- 文件接收:后端Controller成功接收到MultipartFile对象。
- 哈希计算:日志中打印出SHA-256哈希值,且数据库中未因重复报错。
- 对象存储:MinIO的
sm-archive-bucket桶内出现了新的文件对象。 - 数据落库:数据库
sm_archive_metadata表中新增了一条记录,且status字段为1。
若上传重复文件,系统应直接抛出异常并在前端提示“档案文件已存在”,至此,三明数字档案馆系统的核心文件处理引擎即搭建完成。