You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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命令直接读取本地目录导入,更适合万级以上图片的大批量场景:

  1. 在你的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}'))
  1. 执行命令即可完成导入:
python manage.py import_product_images /你的/SKU目录/根路径/

内容的提问来源于stack exchange,提问作者Lone Rider

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 22:48:02