Linux挂载NAS时Python openpyxl读存在的xlsx报文件不存在
openpyxl读取xlsx文件抛出
no such file or directory错误 异常表现
- 所有相关文件(.py脚本、.xlsx数据文件)均存储在NAS设备上,脚本逻辑为读取多个不同目录下的.xlsx文件
- 在macOS系统下通过SMB协议访问NAS运行脚本时,所有流程运行正常,文件均可正常读取
- 在服务器部署的Linux虚拟机中,通过mount挂载方式访问NAS运行脚本时,前2个.xlsx文件可正常读取,读取第3个文件时触发报错。该异常文件是从Gmail邮件中下载保存的,手动可正常打开无损坏,经核对文件名100%匹配,Python运行的工作目录配置正确。
相关代码
Gmail邮箱下载保存附件实现
mail = imaplib.IMAP4_SSL("imap.gmail.com" ) mail.login("mail@gmail.com","password") mail.select('INBOX') data = mail.search(None, 'ALL') mail_ids = data[1] id_list = mail_ids[0].split() if len(id_list)>=1: first_email_id = int(id_list[0]) latest_email_id = int(id_list[-1]) for i in range(latest_email_id,first_email_id-1, -1): data = mail.fetch(str(i), '(RFC822)' ) for response_part in data: arr = response_part[0] if isinstance(arr, tuple): msg = email.message_from_string(str(arr[1],'utf-8')) email_subject = msg['subject'] email_to = msg['to'] counter = 1 for part in msg.walk(): # multipart/* are just containers if part.get_content_maintype() == 'multipart': continue filename = part.get_filename() if not filename: ext = mimetypes.guess_extension(part.get_content_type()) if not ext: # Use a generic bag-of-bits extension ext = '.bin' filename = f'part-{counter:03d}{ext}' counter += 1 if ".xls" in filename: with open(os.path.join(".", filename), 'wb') as fp: fp.write(part.get_payload(decode=True))
报错触发位置代码
if xyz == 1: Excel = "Gleichmäßigkeit_A+R.xlsx" ... os.chdir("Klassen") TKlasse1 = lw(Excel) # this file cant be opened, "lw" for openpyxl.load_workbook TKlasse1 = TKlasse1["WL1"]
完整报错输出
paul@elementary:/mnt/paul/server/dhm$ python3 DHM.py Traceback (most recent call last): File "DHM.py", line 347, in <module> TKlasse1 = lw(Excel) File "/usr/local/lib/python3.8/dist-packages/openpyxl/reader/excel.py", line 315, in load_workbook reader = ExcelReader(filename, read_only, keep_vba, File "/usr/local/lib/python3.8/dist-packages/openpyxl/reader/excel.py", line 124, in __init__ self.archive = _validate_archive(fn) File "/usr/local/lib/python3.8/dist-packages/openpyxl/reader/excel.py", line 96, in _validate_archive archive = ZipFile(filename, 'r') File "/usr/lib/python3.8/zipfile.py", line 1251, in __init__ self.fp = io.open(file, filemode) FileNotFoundError: [Errno 2] No such file or directory: 'Gleichmäßigkeit_A+R.xlsx'
问题根因
- 附件文件名未做规范解码:直接调用
part.get_filename()获取带变音符号(比如ä)的文件名时,没有处理邮件头的编码格式,保存到NAS上的文件名和代码中硬写的文件名字节序列不一致。肉眼看两个文件名完全相同,但其中的特殊字符一个是Unicode预组合形式、一个是基础字符+变音标记的组合形式,属于典型的Unicode同形异码问题。 - 系统/协议的文件名兼容逻辑差异:macOS的SMB客户端默认会自动做文件名Unicode标准化(NFC/NFD自动转换),哪怕文件名字节序列有差异也能匹配到文件;Linux下mount挂载NAS时默认不会做这类自动转换,前两个文件名不含特殊字符所以读取正常,第三个带变音符号的文件无法匹配,最终抛出文件不存在的错误。
修复方案
- 修正附件下载逻辑:不要直接使用
part.get_filename()的返回值,引入邮件头解码逻辑,保证保存到磁盘的文件名是标准UTF-8编码字符串,替换文件名获取部分的代码如下:
from email.header import decode_header from email.utils import unquote raw_filename = part.get_filename() filename = "" if raw_filename: decoded_parts = decode_header(unquote(raw_filename)) for content, encoding in decoded_parts: if isinstance(content, bytes): filename += content.decode(encoding or "utf-8") else: filename += content else: ext = mimetypes.guess_extension(part.get_content_type()) if not ext: ext = '.bin' filename = f'part-{counter:03d}{ext}'
- 存量文件处理:如果已经把文件保存到NAS上,进入Linux虚拟机的
Klassen目录,执行ls | od -c查看目标文件的实际文件名字节,要么把代码里的文件名替换为和实际存储完全一致的字符串,要么直接重命名文件,尽量使用纯ASCII字符命名,规避特殊字符的编码兼容问题。 - 编码习惯优化:尽量避免使用
os.chdir()切换工作目录,改用os.path.join()拼接文件的绝对路径传入load_workbook,减少工作目录意外变动导致的路径错误。
内容的提问来源于stack exchange,提问作者user18977494
相关产品推荐
相关产品推荐

