Python修复WordPress站点迁移后中文名附件无法显示问题的小工具

在上一片帖子中,我向 V 友们请教了如何解决压缩文档导致文件名编码错误,从而使 WordPress 中文名附件无法正常被加载的问题(当时我在站点迁移之后出现站内图片大量 404 的情况)。

大家的建议都很好: 不应该使用中文附件名;压缩站点应该使用 tar 而不是 zip 等等,我受益匪浅。但站点图片 404 的问题一直都没能够解决。

之后有一天在 Google 上找到一片文章( Git 上有原文链接,这里就不放了),通过修改文件名以及数据库中文件名记录来修复这个问题。作者也提供了一个工具,但是很遗憾,我试了 4 台电脑,都没能成功运行(运行中出错)。

于是今天闲下来就用 Python 重写了一遍这个工具,并且发到了 Git 上。希望对大家能有用。同时也感谢原作者 WP 大学的 @木子 提供的思路。

Github 地址: https://github.com/guiqiqi/WPChineseAttachFix

祝大家使用愉快 O(∩_∩)O~~


Python修复WordPress站点迁移后中文名附件无法显示问题的小工具

1 回复

这个问题我遇到过,WordPress迁移后中文附件不显示,主要是因为文件名编码问题。Windows服务器用GBK,Linux用UTF-8,迁移时文件名没正确转换就会404。

核心解决思路:遍历数据库附件记录,把中文文件名从UTF-8转成GBK(或反向),同时重命名服务器上的实际文件。

下面这个工具直接拿去用:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WordPress中文附件修复工具
用于解决站点从Windows迁移到Linux(或反向)后中文名附件无法访问的问题
"""

import pymysql
import os
import sys
from urllib.parse import unquote

class WordPressAttachmentFixer:
    def __init__(self, db_config):
        """
        初始化数据库连接
        :param db_config: 字典,包含host, user, password, database
        """
        self.conn = pymysql.connect(**db_config)
        self.cursor = self.conn.cursor()
        
    def get_attachments_with_chinese(self):
        """获取所有包含中文字符的附件记录"""
        query = """
        SELECT post_id, meta_value 
        FROM wp_postmeta 
        WHERE meta_key = '_wp_attached_file'
        AND meta_value REGEXP '[\\x{4e00}-\\x{9fa5}]'
        """
        self.cursor.execute(query)
        return self.cursor.fetchall()
    
    def convert_filename_encoding(self, filename, from_encoding='utf-8', to_encoding='gbk'):
        """
        转换文件名编码
        :param filename: 原始文件名(URL解码后的)
        :param from_encoding: 当前编码
        :param to_encoding: 目标编码
        :return: (转换后的文件名, 是否需要重命名文件)
        """
        try:
            # URL解码
            decoded = unquote(filename)
            
            # 如果已经是目标编码,直接返回
            try:
                decoded.encode(to_encoding)
                return decoded, False
            except:
                pass
                
            # 转换编码
            if from_encoding.lower() == 'utf-8' and to_encoding.lower() == 'gbk':
                # UTF-8 -> GBK
                converted = decoded.encode('utf-8').decode('gbk', errors='ignore')
            elif from_encoding.lower() == 'gbk' and to_encoding.lower() == 'utf-8':
                # GBK -> UTF-8
                converted = decoded.encode('gbk').decode('utf-8', errors='ignore')
            else:
                return decoded, False
                
            return converted, converted != decoded
            
        except Exception as e:
            print(f"编码转换失败 {filename}: {e}")
            return filename, False
    
    def update_database_record(self, post_id, new_filename):
        """更新数据库中的附件记录"""
        update_query = """
        UPDATE wp_postmeta 
        SET meta_value = %s
        WHERE meta_key = '_wp_attached_file' 
        AND post_id = %s
        """
        self.cursor.execute(update_query, (new_filename, post_id))
        self.conn.commit()
        
    def rename_physical_file(self, wp_content_path, old_path, new_path):
        """重命名服务器上的物理文件"""
        old_full = os.path.join(wp_content_path, 'uploads', old_path)
        new_full = os.path.join(wp_content_path, 'uploads', new_path)
        
        # 创建目标目录(如果需要)
        os.makedirs(os.path.dirname(new_full), exist_ok=True)
        
        if os.path.exists(old_full):
            os.rename(old_full, new_full)
            print(f"文件重命名: {old_path} -> {new_path}")
            return True
        else:
            print(f"文件不存在: {old_full}")
            return False
    
    def fix_attachments(self, wp_content_path, from_encoding='utf-8', to_encoding='gbk'):
        """
        主修复函数
        :param wp_content_path: WordPress的wp-content目录绝对路径
        :param from_encoding: 当前文件名编码
        :param to_encoding: 目标编码
        """
        attachments = self.get_attachments_with_chinese()
        print(f"找到 {len(attachments)} 个中文附件需要处理")
        
        fixed_count = 0
        for post_id, filepath in attachments:
            print(f"\n处理附件 ID {post_id}: {filepath}")
            
            # 转换文件名编码
            new_filename, need_rename = self.convert_filename_encoding(
                filepath, from_encoding, to_encoding
            )
            
            if need_rename:
                # 更新数据库
                self.update_database_record(post_id, new_filename)
                
                # 重命名物理文件
                if self.rename_physical_file(wp_content_path, filepath, new_filename):
                    fixed_count += 1
                    print(f"✓ 已修复: {filepath}")
                else:
                    print(f"✗ 文件重命名失败")
            else:
                print(f"- 无需处理")
        
        print(f"\n处理完成!共修复 {fixed_count} 个附件")
        
    def close(self):
        """关闭数据库连接"""
        self.cursor.close()
        self.conn.close()

# 使用示例
if __name__ == "__main__":
    # 配置参数(根据实际情况修改)
    DB_CONFIG = {
        'host': 'localhost',
        'user': 'your_db_user',
        'password': 'your_db_password',
        'database': 'your_db_name',
        'charset': 'utf8mb4'
    }
    
    # WordPress的wp-content目录绝对路径
    WP_CONTENT_PATH = '/var/www/html/wp-content'
    
    # 编码转换方向(根据迁移方向选择)
    # 从Linux迁移到Windows: from_encoding='utf-8', to_encoding='gbk'
    # 从Windows迁移到Linux: from_encoding='gbk', to_encoding='utf-8'
    
    fixer = WordPressAttachmentFixer(DB_CONFIG)
    
    try:
        # 示例:从Windows迁移到Linux(GBK -> UTF-8)
        fixer.fix_attachments(
            wp_content_path=WP_CONTENT_PATH,
            from_encoding='gbk',
            to_encoding='utf-8'
        )
    finally:
        fixer.close()

使用步骤:

  1. 修改脚本中的数据库配置和wp-content路径
  2. 根据迁移方向设置编码转换方向
  3. 先备份数据库和wp-content/uploads目录
  4. 运行脚本:python fix_attachments.py

注意: 如果附件在媒体库有多个尺寸,还需要处理_wp_attachment_metadata中的记录,上面的脚本可以扩展这个功能。

一句话建议:先备份再操作,根据迁移方向选对编码转换参数。

回到顶部