You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIL.save()报错:填充字符需为Unicode而非字节的问题排查

解决EXIF元数据修改时的"The fill character must be a unicode character, not bytes"错误

问题背景

  • 需求:依据YYYYMMDD_hhmmdd格式的图片文件名,修改照片的拍摄日期、创建日期等元数据。
  • 异常情况:代码处理含二进制GPS信息的图片(20190803_212800.jpeg)时正常,但处理无GPS信息的图片(20190805_120100.jpeg)时,抛出错误:The fill character must be a unicode character, not bytes。
  • 定位结论:问题与图片是否包含GPS信息直接相关,无GPS信息的图片EXIF结构缺失引发编码冲突。

原代码

import os
from datetime import datetime
from PIL import Image

Date_dt_Format = "%Y:%m:%d %H:%M:%S"
Name_format = "%Y%m%d_%H%M%S"

def Change_Property_picture(File_Name_dt: datetime, file_path: str):
    # read the image data using PIL
    image = Image.open(file_path)
    DateTime_import = File_Name_dt.strftime(Date_dt_Format)

    # extract EXIF data
    exif1 = image.getexif()

    # Update TAGs (34665 --> Exif)
    exif1.get_ifd(34665)[306] = DateTime_import
    exif1.get_ifd(34665)[36867] = DateTime_import
    exif1.get_ifd(34665)[36868] = DateTime_import
    exif1.get_ifd(34665)[50971] = DateTime_import

    creation_time = File_Name_dt.timestamp()
    modification_time = File_Name_dt.timestamp()
    
    # Save
    image.save(fp=f"{file_path}", exif=exif1)
    os.utime(file_path, (creation_time, modification_time))

picture_path = "path to picture"
Pict1_dt = datetime.strptime("20190803_212800", Name_format)
Pict2_dt = datetime.strptime("20190805_120100", Name_format)
Change_Property_picture(File_Name_dt=Pict1_dt ,file_path=f"{picture_path}20190803_212800.jpeg")
Change_Property_picture(File_Name_dt=Pict2_dt ,file_path=f"{picture_path}20190805_120100.jpeg")

错误原因

原代码直接操作PIL的get_ifd(34665)获取Exif子IFD,但无GPS信息的图片EXIF结构不完整,PIL在序列化EXIF数据时,会出现字节与Unicode字符的编码冲突,触发填充字符类型错误。而含GPS信息的图片EXIF结构完整,序列化流程无异常。

解决方案

方案1:修复PIL代码逻辑

通过提前检查并初始化必要的EXIF子IFD,避免结构缺失引发的编码问题:

import os
from datetime import datetime
from PIL import Image

Date_dt_Format = "%Y:%m:%d %H:%M:%S"
Name_format = "%Y%m%d_%H%M%S"

def Change_Property_picture(File_Name_dt: datetime, file_path: str):
    image = Image.open(file_path)
    DateTime_import = File_Name_dt.strftime(Date_dt_Format)

    # 获取EXIF数据,若不存在则初始化空字典
    exif = image.getexif()
    # 确保Exif子IFD(标签34665)存在,不存在则创建空字典
    if 34665 not in exif:
        exif[34665] = {}
    exif_ifd = exif.get_ifd(34665)
    
    # 更新日期相关标签
    exif_ifd[306] = DateTime_import       # DateTime(图片修改日期)
    exif_ifd[36867] = DateTime_import     # DateTimeOriginal(拍摄日期)
    exif_ifd[36868] = DateTime_import     # DateTimeDigitized(数字化日期)
    exif_ifd[50971] = DateTime_import     # SubsecTimeOriginal(拍摄日期子秒)

    # 更新文件系统时间戳
    timestamp = File_Name_dt.timestamp()
    os.utime(file_path, (timestamp, timestamp))
    
    # 保存时明确指定格式,避免编码歧义
    image.save(file_path, exif=exif, format="JPEG")

picture_path = "path to picture"
Pict1_dt = datetime.strptime("20190803_212800", Name_format)
Pict2_dt = datetime.strptime("20190805_120100", Name_format)
Change_Property_picture(Pict1_dt, f"{picture_path}20190803_212800.jpeg")
Change_Property_picture(Pict2_dt, f"{picture_path}20190805_120100.jpeg")

方案2:使用piexif库(更稳定)

piexif是专门处理EXIF数据的库,对边缘情况支持更完善,避免PIL的EXIF序列化bug:

  1. 先安装库:pip install piexif
  2. 替换代码:
import os
import piexif
from datetime import datetime
from PIL import Image

Date_dt_Format = "%Y:%m:%d %H:%M:%S"
Name_format = "%Y%m%d_%H%M%S"

def Change_Property_picture(File_Name_dt: datetime, file_path: str):
    image = Image.open(file_path)
    DateTime_import = File_Name_dt.strftime(Date_dt_Format)
    
    # 读取现有EXIF数据,无数据则初始化空结构
    try:
        exif_dict = piexif.load(image.info["exif"])
    except KeyError:
        exif_dict = {"Exif": {}, "0th": {}, "GPS": {}, "1st": {}}
    
    # 更新各日期标签
    exif_dict["0th"][piexif.ImageIFD.DateTime] = DateTime_import               # 图片修改日期
    exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = DateTime_import       # 拍摄日期
    exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized] = DateTime_import     # 数字化日期
    exif_dict["Exif"][50971] = DateTime_import                                 # 拍摄日期子秒
    
    # 将字典转换为EXIF字节流
    exif_bytes = piexif.dump(exif_dict)
    
    # 保存图片并写入新EXIF数据
    image.save(file_path, exif=exif_bytes, format="JPEG")
    
    # 更新文件系统时间戳
    timestamp = File_Name_dt.timestamp()
    os.utime(file_path, (timestamp, timestamp))

picture_path = "path to picture"
Pict1_dt = datetime.strptime("20190803_212800", Name_format)
Pict2_dt = datetime.strptime("20190805_120100", Name_format)
Change_Property_picture(Pict1_dt, f"{picture_path}20190803_212800.jpeg")
Change_Property_picture(Pict2_dt, f"{picture_path}20190805_120100.jpeg")

内容的提问来源于stack exchange,提问作者Jan Vaško

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:02:05