工作中特殊场景下的黑魔法

前言

  工作中偶尔会遇到一些特殊需求需要解决,这里记录一下。

需求篇

Mac 修改文件创建时间和修改时间

  使用 setfile 命令:

修改创建日期:setfile -d "mm/dd/yy hh:mm:ss" filename

修改修改日期:setfile -m "mm/dd/yy hh:mm:ss" filename

同时修改 xxx.txt 文件两个时间为 2023-07-27 01:23:53:

setfile -d "07/27/2023 01:23:53" -m "07/27/2023 01:23:53" ./xxx.txt

Excel 修改创建时间

  word 和 excel 本质上都是 zip 文件,可利用 openpyxl 修改 xlsx 文件元信息创建时间。对于 xls 文件,若文件有密码,需先去除密码,再将 xls 转换为 xlsx 文件,之后使用 openpyxl 修改时间。具体步骤如下:

  1. 用 AppleScript 将 xls 转换为 xlsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def run_applescript(script):
    """运行 AppleScript 脚本"""
    subprocess.run(["osascript", "-e", script])

    def xls_to_xlsx(file_path="./xxx.xls"):
    """使用 AppleScript 修改 Excel 文件元数据"""
    applescript = f'''
    tell application "Microsoft Excel"
    -- 打开 .xls 文件
    set inputFile to "{file_path}" -- 修改为你的文件路径
    open inputFile

    -- 获取当前工作簿
    set wb to active workbook

    -- 定义输出文件路径
    set outputFile to "{file_path}x" -- 修改为你想保存的文件路径

    -- 保存为 .xlsx 格式
    save workbook as wb filename outputFile file format Excel XML file format

    -- 关闭工作簿
    close wb saving no
    end tell
    '''
    run_applescript(applescript)
  2. 修改 xlsx 文件创建时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import openpyxl

    def modify_excel_metadata(file_path = "./xxx.xlsx"):
    # 打开 Excel 文件
    wb = openpyxl.load_workbook(file_path)

    # 获取元数据(properties)
    # properties = wb.properties
    # print(properties.__dict__)
    dt = datetime.strptime("2023-01-07 14:00:45", "%Y-%m-%d %H:%M:%S")
    dt -= timedelta(hours=8)
    wb.properties.creator = ""
    wb.properties.modified = dt
    wb.properties.created = dt
    wb.save("./xxx_tmp.xlsx")
  3. 将 xlsx 转换为 xls 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def xlsx_to_xls(file_path="./xxx_tmp.xlsx"):
    """使用 AppleScript 修改 Excel 文件元数据"""
    applescript = f'''
    tell application "Microsoft Excel"
    -- 打开 .xls 文件
    set inputFile to "{file_path}" -- 修改为你的文件路径
    open inputFile

    -- 获取当前工作簿
    set wb to active workbook

    -- 定义输出文件路径
    set xlsFilePath to (inputFile as text)
    set xlsFilePath to text 1 thru -6 of xlsFilePath -- 去掉 ".xlsx"
    set xlsFilePath to xlsFilePath & ".xls"
    # log xlsFilePath

    -- 保存为 .xls 格式
    save wb in xlsFilePath
    # save workbook as wb filename xlsFilePath file format Excel98to2004 file format with overwrite

    -- 关闭工作簿
    close wb saving yes
    end tell
    '''
    run_applescript(applescript)

JPG 修改创建时间

  利用 pillow 和 piexif 修改 jpg 文件 exif 信息时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image
import piexif

def modify_jpg_exif(img_file="./xxx.jpg", time_str = "2023:01:07 14:00:45"):
im = Image.open(img_file)
if "exif" not in im.info:
return
exif_dict = piexif.load(im.info["exif"])

# for ifd in ("0th", "Exif", "GPS", "1st"):
# for tag in exif_dict[ifd]:
# print(ifd, tag, piexif.TAGS[ifd][tag], exif_dict[ifd][tag])

del exif_dict["1st"]
del exif_dict["thumbnail"]

exif_dict["0th"][piexif.ImageIFD.DateTime] = time_str.encode()
exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = time_str.encode()
exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized] = time_str.encode()
exif_bytes = piexif.dump(exif_dict)
im.save("./xxx_m.jpg", exif=exif_bytes, quality='keep', subsampling='keep')

  Pillow 保存 jpg 图片默认会同时保存 JFIF 和 EXIF 头,若需要去掉 JFIF 头,需修改 Pillow JpegEncode.c 文件源码:

1
2
3
4
5
6
if (context->xdpi > 0 && context->ydpi > 0) {
context->cinfo.write_JFIF_header = TRUE;
context->cinfo.density_unit = 1; /* dots per inch */
context->cinfo.X_density = context->xdpi;
context->cinfo.Y_density = context->ydpi;
}

修改为:

1
context->cinfo.write_JFIF_header = FALSE;

之后执行:python3 -m pip -v install . 从本地源码安装 Pillow。

PDF 修改创建时间

  使用 pikepdf 修改 pdf 文件元信息时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pikepdf

def modify_pdf_metadata(file_path="./xxx.pdf", time_str = "20241203140045+08'00'"):
# 打开 PDF 文件
with pikepdf.open(file_path, allow_overwriting_input=True) as pdf:
## 获取 PDF 的元数据
# metadata = pdf.docinfo
# for key, value in metadata.items():
# print(f'{key}: {value}')

# 修改元数据
pdf.docinfo["/CreationDate"] = time_str
pdf.docinfo["/ModDate"] = time_str
# 保存文件
pdf.save()