使用Python zipfile打包的文件上传AWS Lambda出现目录嵌套问题
问题成因
核心问题是打包时写入zip的内部路径逻辑错误,生成的zip包本身内部就自带了一层根目录
你代码中计算zip内部存储路径用的基准是os.path.join(folders[0], '../..'),这个路径等价于当前脚本执行目录的上一级目录。假设你执行脚本的工作目录名称就是project-lambda-preSignUpTrigger,里面直接放着main、pymysql文件夹,那么对于main目录下的任意文件,计算得到的相对路径都会是project-lambda-preSignUpTrigger/main/xxx,也就是说你打出来的zip包内部本身就嵌套了一层名为project-lambda-preSignUpTrigger的目录。本地解压的行为误导了你的判断
绝大多数桌面端解压工具默认会对zip包做特殊处理:如果检测到zip包内部只有一个根目录,会默认把这个根目录的名称替换成zip包的文件名,或者直接把内部根目录的内容解压到以zip文件名命名的新建目录下。你本地看到的project-lambda-preSignUpTrigger目录其实是解压工具生成的,你误以为内部的main、pymysql是直接放在这个目录下,但实际zip包内部本身就多了一层目录结构。
而AWS Lambda的解压逻辑是完全按zip包内部的原始路径展开,不会做任何额外的目录转换,所以你上传后就会看到多了一层嵌套目录。你从控制台导出的zip包内部是没有这层额外根目录的,直接就是main和pymysql在根路径,所以重新上传就不会有问题。
修复方案
你只需要把相对路径的基准改成当前脚本执行的工作目录即可,修改后代码如下:
import os import zipfile def zipit(folders, zip_filename): zip_file = zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) # 基准路径改成当前工作目录 base_path = os.getcwd() for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): for filename in filenames: full_path = os.path.join(dirpath, filename) # 计算相对于当前工作目录的路径,直接作为zip内的存储路径 rel_path = os.path.relpath(full_path, base_path) zip_file.write(full_path, rel_path) zip_file.close() folders = [ "main", "pymysql"] if __name__ == "__main__": zipit(folders, "project-lambda-preSignUpTrigger.zip")
验证打包是否正确的方法:不要直接双击解压,用命令行执行unzip project-lambda-preSignUpTrigger.zip,看输出的路径结构是不是直接输出main/xxx和pymysql/xxx,没有额外的根目录即可。
内容的提问来源于stack exchange,提问作者L44TXF

