Django应用中单独线程执行CSV处理函数时触发ValueError
解决Django线程中处理CSV压缩包时的ValueError问题
大概率你遇到的问题和Django上传文件的生命周期以及线程执行时机密切相关,我来拆解下可能的原因和对应的解决办法:
最核心的原因:请求结束后上传文件被自动清理
Django里request.FILES中的上传文件对象是和当前请求周期绑定的。当视图函数执行完毕、响应返回给客户端后,Django会自动关闭并删除这些临时文件。如果你的新线程是在视图返回后才开始处理文件,此时文件已经不存在,就会抛出ValueError(比如尝试读取已关闭的文件句柄,或者访问已被删除的临时文件)。
另外,你传给线程的csv_files如果是基于上传文件创建的ZipFile实例,同样会因为底层临时文件被清理而失效。
解决方案:先保存文件到本地再交给线程处理
你需要先把上传的压缩包保存到服务器的本地目录(临时目录或固定目录均可),然后让线程读取本地文件,而非直接使用请求中的文件对象。
1. 修改视图逻辑,先保存上传文件
# views.py import os import tempfile import zipfile from threading import Thread from django.http import HttpResponse from .update_db import upload_files def handle_upload(request): if request.method == 'POST' and request.FILES.get('zip_file'): # 保存上传的压缩包到本地临时文件 zip_upload = request.FILES['zip_file'] with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as tmp_file: # 分块写入避免内存溢出 for chunk in zip_upload.chunks(): tmp_file.write(chunk) tmp_file_path = tmp_file.name # 启动线程,传入本地文件路径 thread = Thread(target=process_zip_async, args=(tmp_file_path,)) thread.start() return HttpResponse("文件处理已启动,请稍后查看数据库更新情况") return HttpResponse("请上传包含CSV的压缩包") def process_zip_async(tmp_file_path): try: # 从本地路径打开ZipFile with zipfile.ZipFile(tmp_file_path, 'r') as csv_files: upload_files(csv_files) finally: # 处理完成后清理临时文件 if os.path.exists(tmp_file_path): os.remove(tmp_file_path)
2. 微调upload_files函数增强稳定性
确保函数能安全处理本地ZipFile实例,同时完善数据库操作的异常处理:
# update_db.py import sqlite3 import csv def upload_files(csv_files): conn = sqlite3.connect('/path/to/db.db') c = conn.cursor() try: for csv_filename in csv_files.namelist(): if csv_filename.endswith('.csv'): with csv_files.open(csv_filename) as csv_file: reader = csv.reader(csv_file) # 跳过表头(如果需要) next(reader, None) # 提取团队名称及其他逻辑 for row in reader: team_name = row[0] # 假设第一列是团队名称 # 写入数据库的逻辑 c.execute("INSERT INTO teams (name) VALUES (?)", (team_name,)) conn.commit() except Exception as e: conn.rollback() # 建议添加日志记录异常,方便排查 print(f"处理CSV时出错: {str(e)}") raise e finally: conn.close()
额外注意事项
- SQLite线程安全:SQLite默认是串行化模式,每个线程单独创建连接是安全的,你当前的代码已经符合要求。
- 线程异常捕获:线程中的异常不会自动抛到主线程,建议在
process_zip_async中添加日志记录,方便后续排查问题。 - 临时文件权限:确保Django进程拥有临时目录的读写权限,避免保存或删除文件时出现权限错误。
内容的提问来源于stack exchange,提问作者chemdog95
相关产品推荐
相关产品推荐

