一、技术架构选型与环境准备
构建企业级数字档案馆系统,核心在于解决海量非结构化数据的存储、元数据管理以及全文检索能力。本文采用Spring Boot 3.x + Vue 3 + MinIO + Elasticsearch的技术栈,实现一套符合OAIS参考模型的自建系统。
1. 基础环境安装
在开始编码前,需在服务器(推荐CentOS 7.9或Ubuntu 20.04)上安装以下基础组件。请直接执行以下命令:
安装JDK 17(后端运行环境):
```bash
wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm
sudo rpm -ivh jdk-17_linux-x64_bin.rpm
```
安装Docker及Docker Compose(用于部署中间件):
```bash
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```
安装Node.js 18(前端构建环境):
```bash
curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
sudo yum install -y nodejs
```
二、数据库设计与初始化
数字档案馆的核心在于档案实体的定义。我们使用MySQL 8.0作为元数据存储库。以下SQL脚本包含了档案门类表和档案实体表的设计,支持自定义字段扩展。
1. 创建数据库与表结构
请在MySQL客户端中直接执行以下SQL语句:
```sql
CREATE DATABASE digital_archive CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE digital_archive;
-- 档案门类定义表(用于定义文书、照片、录音等不同类型)
CREATE TABLE archive_category (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
category_code VARCHAR(50) NOT NULL COMMENT '门类编码,如: WS, ZP',
category_name VARCHAR(100) NOT NULL COMMENT '门类名称',
metadata_config JSON COMMENT '元数据字段配置JSON',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_code (category_code)
) ENGINE=InnoDB;
-- 档案实体主表
CREATE TABLE archive_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
category_id BIGINT NOT NULL COMMENT '关联的门类ID',
title VARCHAR(255) NOT NULL COMMENT '题名',
archive_code VARCHAR(100) COMMENT '档号',
file_path VARCHAR(500) COMMENT '原始文件存储路径',
file_hash VARCHAR(64) COMMENT '文件SHA256校验值',
file_size BIGINT COMMENT '文件大小(字节)',
status TINYINT DEFAULT 0 COMMENT '状态: 0-草稿, 1-已归档',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_category (category_id),
INDEX idx_code (archive_code)
) ENGINE=InnoDB;
-- 插入默认门类数据
INSERT INTO archive_category (category_code, category_name, metadata_config) VALUES
('WS', '文书档案', '[{"field":"year","label":"年度","type":"int"},{"field":"retention","label":"保管期限","type":"string"}]');
```
三、后端核心功能开发
后端使用Spring Boot 3.2进行开发。我们需要实现文件上传、元数据保存以及对象存储的集成。
1. 项目依赖配置
在pom.xml中添加必要的依赖,确保版本一致以避免冲突:
```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.projectlombok
lombok
true
```
2. 配置文件设置
在src/main/resources/application.yml中配置数据库连接及MinIO参数。请确保IP地址和密码与实际环境一致:
```yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/digital_archive?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: YourStrongPassword123
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: archive-files
```
3. 文件上传与归档核心代码
创建ArchiveService.java,实现文件上传至MinIO并将元数据落库的逻辑。这是系统最核心的实操部分:
```java
package com.digital.archive.service;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
@Service
public class StorageService {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Value("${minio.bucketName}")
private String bucketName;
public MinioClient getClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
/
上传文件并返回存储路径
/
public String uploadFile(MultipartFile file, String categoryCode) throws Exception {
MinioClient minioClient = getClient();
String fileName = file.getOriginalFilename();
// 构建层级存储路径:门类/年月/UUID_文件名
String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
String objectName = categoryCode + "/" + datePath + "/" + UUID.randomUUID() + "_" + fileName;
try (InputStream inputStream = file.getInputStream()) {
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
}
return objectName;
}
/
删除文件
/
public void deleteFile(String objectName) throws Exception {
MinioClient minioClient = getClient();
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
}
}
```

创建ArchiveController.java,对外暴露REST API接口:
```java
package com.digital.archive.controller;
import com.digital.archive.entity.ArchiveRecord;
import com.digital.archive.repository.ArchiveRecordRepository;
import com.digital.archive.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.;
import org.springframework.web.multipart.MultipartFile;
import java.util.Optional;
@RestController
@RequestMapping("/api/archive")
public class ArchiveController {
@Autowired
private StorageService storageService;
@Autowired
private ArchiveRecordRepository repository;
/
档案归档接口
/
@PostMapping("/upload")
public String uploadArchive(
@RequestParam("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("categoryId") Long categoryId) {
try {
// 1. 上传文件到对象存储
String categoryCode = "WS"; // 实际应根据categoryId查询
String filePath = storageService.uploadFile(file, categoryCode);
// 2. 保存元数据到数据库
ArchiveRecord record = new ArchiveRecord();
record.setCategoryId(categoryId);
record.setTitle(title);
record.setFilePath(filePath);
record.setFileSize(file.getSize());
record.setStatus(1); // 已归档
repository.save(record);
return "归档成功,文件ID: " + record.getId();
} catch (Exception e) {
e.printStackTrace();
return "归档失败: " + e.getMessage();
}
}
}
```
四、对象存储与检索中间件部署
使用Docker Compose一键部署MinIO(文件存储)和Elasticsearch(全文检索)。创建docker-compose.yml文件:
```yaml
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: digital_minio
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
container_name: digital_es
ports:
- "9200:9200"
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es_data:/usr/share/elasticsearch/data
volumes:
minio_data:
es_data:
```
启动中间件服务:
```bash
docker-compose up -d
```
启动后,访问 http://服务器IP:9001 可进入MinIO控制台管理文件。为了确保Java后端能正常连接,请提前在MinIO控制台中创建名为archive-files的Bucket。
五、前端交互界面实现
使用Vue 3和Element Plus构建简单的上传界面。执行以下命令初始化项目:
```bash
npm create vue@latest archive-frontend
cd archive-frontend
npm install element-plus axios
```
修改src/App.vue,编写完整的上传组件代码:
```html
数字档案馆 - 档案归档
拖拽文件到此处或 点击上传
提交归档
```
启动前端服务进行测试:
```bash
npm run dev
``>
六、系统部署与验证
完成开发后,将后端打包为JAR包,前端打包为静态资源并部署至Nginx。
1. 后端打包
在项目根目录执行:
```bash
mvn clean package -DskipTests
```
生成的JAR包位于target/目录下。运行命令启动:
```bash
java -jar target/your-project-name-0.0.1-SNAPSHOT.jar
```
2. 前端打包与Nginx配置
在前端项目目录执行:
```bash
npm run build
```
将生成的dist目录内容上传至服务器的/var/www/archive/。编辑Nginx配置:
```nginx
server {
listen 80;
server_name your-archive-domain.com;
location / {
root /var/www/archive;
index index.html;
try_files $uri $uri/ /index.html;
}
反向代理后端接口,解决跨域问题
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
重启Nginx使配置生效:
```bash
sudo systemctl restart nginx
```