Django如何批量上传嵌套目录图片并关联对应SKU产品
实现方案
1. 前端模板修改(products_import.html)
最稳妥的批量上传方案是让用户将整个SKU目录打包为zip压缩包上传,避免浏览器多文件/目录上传的兼容性问题。首先修改表单配置,保留原有CSV上传字段,新增压缩包上传项:
<!-- 表单必须添加enctype属性支持文件上传 --> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <!-- 原有CSV上传字段保持不变 --> <div class="form-group"> <label>上传SKU图片压缩包(zip格式)</label> <input type="file" name="image_zip" accept=".zip"> <p class="text-muted">压缩包根目录下为SKU命名的子目录,子目录内放对应产品图片</p> </div> <button type="submit" class="btn btn-primary">提交导入</button> </form>
如果不想打包zip,也可以使用前端目录选择功能,给input添加
webkitdirectory属性即可,此时需要在后端从文件的name属性中解析出SKU目录名,逻辑和zip处理类似。
2. 视图逻辑修改(views.py)
首先导入需要的依赖包,在原有产品导入逻辑后新增图片批量关联逻辑:
# 顶部新增导包 import zipfile import os import shutil from django.conf import settings from django.core.files import File def products_import(request): heading = 'Upload Products' info = '''This importer will import the following fields: sku, name, price, quantity from a csv file.''' if request.method == 'POST': paramFile = io.TextIOWrapper(request.FILES['file'].file, encoding='latin-1') product = csv.DictReader(paramFile) list_of_dict = list(product) objs = [ Product( sku=row['sku'], name=row['name'], price=row['price'], quantity=row['quantity'], ) for row in list_of_dict ] record_count = len(list_of_dict) try: msg = Product.objects.bulk_create(objs) messages.success(request, str(record_count) + ' 条产品记录上传成功') except Exception as e: messages.error(request, f'产品数据导入失败: {str(e)}') return redirect('products_import') # ------------------- 新增图片批量导入逻辑 ------------------- if 'image_zip' in request.FILES: zip_file = request.FILES['image_zip'] # 临时解压目录 temp_dir = os.path.join(settings.MEDIA_ROOT, 'temp_zip_import') os.makedirs(temp_dir, exist_ok=True) img_count = 0 fail_skus = [] try: # 解压压缩包 with zipfile.ZipFile(zip_file, 'r') as zf: zf.extractall(temp_dir) # 遍历第一层SKU目录 for sku_name in os.listdir(temp_dir): sku_path = os.path.join(temp_dir, sku_name) if not os.path.isdir(sku_path): continue # 匹配对应SKU的产品 try: product = Product.objects.get(sku=sku_name.strip()) except Product.DoesNotExist: fail_skus.append(sku_name) continue # 遍历目录下所有图片文件 for img_file in os.listdir(sku_path): img_path = os.path.join(sku_path, img_file) if os.path.isfile(img_path) and img_file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')): with open(img_path, 'rb') as f: Image.objects.create( product=product, image=File(f, name=img_file) ) img_count += 1 messages.success(request, f'共成功导入 {img_count} 张产品图片') if fail_skus: messages.warning(request, f'以下SKU未找到对应产品,图片未导入:{", ".join(fail_skus)}') except Exception as e: messages.error(request, f'图片导入失败:{str(e)}') finally: # 清理临时解压文件 if os.path.exists(temp_dir): shutil.rmtree(temp_dir) return redirect('products_import') context = {'heading': heading, 'info': info} return render(request, 'coreapp/product/products_import.html', context)
注意事项
- 需要确保
settings.py中MEDIA_ROOT、MEDIA_URL配置正确,ImageField可正常存储文件 - 大体积压缩包上传需要调整上传大小限制:修改
settings.py中FILE_UPLOAD_MAX_MEMORY_SIZE、DATA_UPLOAD_MAX_MEMORY_SIZE参数,同时调整反向代理(如Nginx)的client_max_body_size配置
3. 可选:服务器本地批量导入命令
如果图片已经存放在服务器上,不需要走web上传,可自定义Django命令直接读取本地目录导入,更适合万级以上图片的大批量场景:
- 在你的app下创建目录结构
management/commands/,新增文件import_product_images.py
from django.core.management.base import BaseCommand from yourapp.models import Product, Image import os from django.core.files import File class Command(BaseCommand): help = '批量从SKU目录导入产品图片' def add_arguments(self, parser): parser.add_argument('img_root_path', type=str, help='SKU目录所在的根目录绝对路径') def handle(self, *args, **options): img_root = options['img_root_path'] img_count = 0 fail_skus = [] for sku_name in os.listdir(img_root): sku_path = os.path.join(img_root, sku_name) if not os.path.isdir(sku_path): continue try: product = Product.objects.get(sku=sku_name.strip()) except Product.DoesNotExist: fail_skus.append(sku_name) continue for img_file in os.listdir(sku_path): img_path = os.path.join(sku_path, img_file) if os.path.isfile(img_path) and img_file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')): with open(img_path, 'rb') as f: Image.objects.create( product=product, image=File(f, name=img_file) ) img_count +=1 self.stdout.write(self.style.SUCCESS(f'成功导入 {img_count} 张图片')) if fail_skus: self.stdout.write(self.style.WARNING(f'未匹配到产品的SKU:{fail_skus}'))
- 执行命令即可完成导入:
python manage.py import_product_images /你的/SKU目录/根路径/
内容的提问来源于stack exchange,提问作者Lone Rider
相关产品推荐
相关产品推荐

