Strapi+MySQL+S3环境下导入Dev库时如何保留Prod文件元数据并同步新媒体
针对你遇到的开发环境数据导入生产时覆盖files表、丢失媒体元数据的问题,以下是几种可落地的通用实践:
方案一:MySQL 基于唯一键的增量导入(推荐自动化)
利用MySQL的INSERT ... ON DUPLICATE KEY UPDATE语法,实现仅新增生产环境不存在的媒体记录,保留现有生产数据:
添加唯一索引
在生产环境的strapi_files表中,针对S3对象键(key字段)或文件哈希值(file_hash字段)创建唯一索引,确保能识别重复的媒体文件:ALTER TABLE strapi_files ADD UNIQUE INDEX idx_unique_file_key (`key`); -- 或用file_hash:ALTER TABLE strapi_files ADD UNIQUE INDEX idx_unique_file_hash (`file_hash`);导出开发环境的媒体表
使用mysqldump单独导出开发环境的strapi_files及关联表(如strapi_upload_file_related_morph):mysqldump -u [DEV_DB_USER] -p [DEV_DB_NAME] strapi_files strapi_upload_file_related_morph > dev_media_dump.sql修改导出的SQL脚本
打开导出的dev_media_dump.sql,将所有INSERT INTO语句修改为带ON DUPLICATE KEY UPDATE的格式,重复记录不做任何更新:-- 原语句:INSERT INTO strapi_files (id, key, file_hash, ...) VALUES (...); -- 修改后: INSERT INTO strapi_files (id, key, file_hash, ...) VALUES (...) ON DUPLICATE KEY UPDATE id = id; -- 保留生产环境现有记录,不覆盖 -- 关联表同理,针对关联唯一键(如file_id + related_id + related_type)设置唯一索引后,同样用ON DUPLICATE KEY UPDATE跳过重复导入到生产环境
执行修改后的SQL脚本完成增量导入:mysql -u [PROD_DB_USER] -p [PROD_DB_NAME] < dev_media_dump.sql
方案二:Strapi 自定义迁移脚本(贴合CMS架构)
通过Strapi的数据库查询API编写脚本,精准同步增量媒体数据,避免直接操作数据库的风险:
开发环境导出增量媒体
编写Node.js脚本,从开发环境获取所有媒体记录,过滤掉生产环境已存在的条目(通过key或file_hash判断):// 开发环境运行的脚本 const strapi = require('@strapi/strapi'); const fs = require('fs'); async function exportIncrementalMedia() { await strapi().load(); // 获取开发所有媒体 const devFiles = await strapi.db.query('plugin::upload.file').findMany({ populate: ['related'], }); // 假设已通过API或数据库查询获取生产环境的所有file_key列表 const prodFileKeys = await fetchProdFileKeys(); // 自行实现该函数 // 过滤出生产不存在的媒体 const incrementalFiles = devFiles.filter(file => !prodFileKeys.includes(file.key)); fs.writeFileSync('incremental_media.json', JSON.stringify(incrementalFiles)); } exportIncrementalMedia();生产环境导入增量媒体
在生产环境运行脚本,将过滤后的媒体数据插入数据库,同时确保S3文件已同步:// 生产环境运行的脚本 const strapi = require('@strapi/strapi'); const fs = require('fs'); async function importIncrementalMedia() { await strapi().load(); const incrementalFiles = JSON.parse(fs.readFileSync('incremental_media.json', 'utf8')); for (const file of incrementalFiles) { // 插入媒体记录 await strapi.db.query('plugin::upload.file').create({ data: { key: file.key, file_hash: file.file_hash, name: file.name, mime: file.mime, // 其他必填字段... }, }); // 插入关联记录(如果有) for (const related of file.related) { await strapi.db.query('plugin::upload.file').createRelations({ where: { id: file.id }, relation: 'related', values: related.id, }); } } } importIncrementalMedia();同步S3文件
在导入数据库前,先将开发环境S3桶的新增文件同步到生产桶:aws s3 sync s3://dev-strapi-uploads s3://prod-strapi-uploads --delete --exclude "*" --include "uploads/**/*"
方案三:拆分备份与导入流程
将数据库备份拆分为普通内容和媒体内容两部分,分别处理:
全量导入非媒体表
导出开发环境除strapi_files和关联表外的所有表,直接导入生产:mysqldump -u [DEV_DB_USER] -p [DEV_DB_NAME] --ignore-table=[DEV_DB_NAME].strapi_files --ignore-table=[DEV_DB_NAME].strapi_upload_file_related_morph > dev_content_dump.sql mysql -u [PROD_DB_USER] -p [PROD_DB_NAME] < dev_content_dump.sql增量导入媒体表
- 先从生产环境导出所有媒体的唯一标识(如
key):mysql -u [PROD_DB_USER] -p [PROD_DB_NAME] -e "SELECT key FROM strapi_files;" > prod_file_keys.txt - 导出开发环境中生产不存在的媒体记录:
mysqldump -u [DEV_DB_USER] -p [DEV_DB_NAME] strapi_files strapi_upload_file_related_morph --where="key NOT IN ('$(paste -s -d "','" prod_file_keys.txt)')" > dev_incremental_media.sql - 将增量媒体导入生产环境:
mysql -u [PROD_DB_USER] -p [PROD_DB_NAME] < dev_incremental_media.sql
- 先从生产环境导出所有媒体的唯一标识(如
关键注意事项
- 主键冲突处理:若开发与生产的
files表id字段有重复,导入时可去掉SQL中的id值,让MySQL自动生成新的自增主键,避免冲突。 - 关联表同步:务必同步
strapi_upload_file_related_morph表的增量记录,否则内容与媒体的关联会失效。 - Jenkins自动化:将上述步骤封装为Shell脚本或Jenkinsfile阶段,集成到CI/CD流水线中,实现一键安全部署。
内容的提问来源于stack exchange,提问作者keerthi m

