Django使用ftplib连接FTP上传文件是否可行?如何解决相关报错
问题根因
这套借助ftplib+Django后台异步任务跨服务传文件的方案完全可行,你两次上传失败的核心原因都是参数传递错误,和框架、组件本身的能力无关:
- 第一版代码触发
AttributeError: 'str' object has no attribute 'read',是因为ftp.storbinary要求第二个参数传入支持read()方法的二进制类文件对象,而你写的file = os.path.join(folder, filename)只是本地文件的路径字符串,根本不是打开的文件句柄,ftplib尝试调用字符串的read方法必然报错。另外这段代码里filename变量没有在函数入参或者内部定义,本身也会触发NameError。 - 第二版用PIL+BytesIO处理的代码逻辑方向是对的,但最后传参传错了:你把处理好的图片写入了BytesIO实例
b,也执行了b.seek(0)把流指针移到了开头,结果传给storbinary的还是PIL打开的file对象,不是准备好的字节流b,自然上传失败。
可直接运行的实现代码
场景1:直接上传本地原文件,不需要额外处理
@background(schedule=1) def uploadToFTP(folder, filename_list): """ 异步上传本地文件到FTP服务器 :param folder: 本地文件存储的绝对路径 :param filename_list: 本次需要上传的文件名列表 """ print("-------------------Start FTP Task ----------------------------") # 建立FTP连接,配置超时避免僵死连接 ftp = FTP( host=conf_settings.FTP_DOMAIN, user=conf_settings.FTP_USER, passwd=conf_settings.FTP_PASSWORD, timeout=30 ) # 开启被动模式,适配绝大多数FTP服务器的防火墙规则 ftp.set_pasv(True) for filename in filename_list: local_path = os.path.join(folder, filename) # 必须以二进制只读模式打开本地文件,拿到合法的文件对象 with open(local_path, 'rb') as f: ftp.storbinary(f'STOR {filename}', fp=f, blocksize=102400) # 上传完成主动关闭连接,避免连接泄漏 ftp.quit()
场景2:图片经PIL处理后直接从内存上传,不生成本地临时文件
import io from PIL import Image @background(schedule=1) def uploadProcessedImgToFTP(folder, img_name_list): print("-------------------Start Processed Image FTP Task ----------------------------") ftp = FTP( host=conf_settings.FTP_DOMAIN, user=conf_settings.FTP_USER, passwd=conf_settings.FTP_PASSWORD, timeout=30 ) ftp.set_pasv(True) for img_name in img_name_list: local_img_path = os.path.join(folder, img_name) with Image.open(local_img_path) as img: # 可按需插入图片处理逻辑:裁剪、压缩、加水印、转格式等 img_buffer = io.BytesIO() img.save(img_buffer, format="JPEG", quality=90) # 关键操作:将字节流指针重置到起始位置,否则会读取到空内容 img_buffer.seek(0) # 注意传入的是准备好的内存字节流对象,不是PIL的Image实例 ftp.storbinary(f'STOR {img_name}', fp=img_buffer, blocksize=102400) img_buffer.close() ftp.quit()
避坑提示
- 不要在任务函数内部使用未定义的变量(比如第一版代码里的
filename),后台调度环境拿不到请求上下文里的临时变量,很容易触发变量未定义错误,所有任务需要的参数都要通过入参显式传入。 - 如果需要上传到FTP的指定子目录,在执行storbinary前先调用
ftp.cwd(远程目标目录路径)切换工作目录,否则文件会默认传到FTP账号的根目录。 - 如果FTP服务器限制了IP连接频率,不要在循环里反复新建/关闭FTP连接,复用同一个连接批量上传即可。
- 不要忘了给打开的本地文件、内存流、FTP连接做资源释放,用with上下文或者手动调用close/quit都可以,不然任务跑久了会出现文件句柄泄漏、连接数超限的问题。
内容的提问来源于stack exchange,提问作者MoLoW
相关产品推荐
相关产品推荐

