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

使用Django和React上传图片遇400错误,寻求解决方案

Django + React 图片上传400错误排查与修复

问题描述

我正尝试用Django和React上传图片及其他表单数据,现在碰到400 Bad Request错误。预期流程是先调用第一个API创建不含图片的产品(这个请求返回200状态码),再调用第二个API给该产品添加图片,但第二个请求返回400错误。我用FileReader让浏览器读取图片URL,以下是我的代码:

React 代码

export default function Add(){

    let [formData , setFormData] = useState({namee: '',price: ''})
    let [files , setFiles] = useState(null)
   
  

    const handleInputChange = (event) => {
        const { name , value } = event.target
        setFormData({...formData, [name] : value})
    }

    const handleImageChange = (event) => {

      const file = event.target.files[0]
      const reader = new FileReader()
      reader.addEventListener("load", function() {
        console.log(reader.result)
        setFiles(reader.result)
      })
      reader.readAsDataURL(file)
      
    }


    const handleSubmit = (event) => {
      
      event.preventDefault()
      const { namee, price } = formData
      const formDataObj1 = new FormData()
      const formDataObj2 = new FormData()
      formDataObj1.append('namee', namee)
      formDataObj1.append('price', price)
      formDataObj2.append('image', files)
      
      if(files){
        fetch('http://127.0.0.1:8000/add' , 
            {
                method: "POST",
                body: formDataObj1,
            }
        )
            .then(response => {
                if(response.ok){
                  
                   fetch('http://127.0.0.1:8000/addimage', 
                   {
                    method: "PUT",
                    body: formDataObj2,
                   })
                   .then(response => {
                    console.log(response)
                    if(response.ok){
                      alert('Your Product has been added :)')
                    }
                    else{
                      console.error('Form submission failed.')
                  }
                   })
                   .catch(error => {
                      console.log(error)
                   })
                }
                else{
                    console.error('Form submission failed.')
                }
            })
                .catch(error => {
                    console.log('Form submission failed.' , error)
                })
    }
    else{
      console.log('files is empty')
    }
  }

    const imgStyle = {
        marginBottom: '10px'
    }

    return (
        <div className="c3">
        <div id="info" className="center">
          <h1>Product</h1>
          <form onSubmit={handleSubmit} >
            <div className="txt_field">
              <input type="text" name="namee" value={formData.namee} onChange={handleInputChange} required/>
              <span></span>
              <label>Name</label>
            </div>
            <div className="txt_field">
              <input type="number" name="price" value={formData.price} onChange={handleInputChange} required/>
              <span></span>
              <label>Price</label> 
            </div>
            <div style={imgStyle}>
              <input type="file" name="image" onChange={handleImageChange}  required/>
              <span></span>
            </div>
            <input  type="submit" value="Confirm" className="ss"/>
          </form>
        </div>
      </div>
    )

}

Django 代码

Views

# views

class AddProduct(generics.CreateAPIView):


    serializer_class = ProductSerializer

    def post(self , request , format=None):
        name = request.data['namee']
        price = request.data['price']
        Product.objects.create(name=name, price=price)
        return Response('product successfuly added', status=status.HTTP_200_OK)


class AddProductImage(APIView):

    serializer_class = AddImageProSerializer
    parser_classes = ( MultiPartParser, parsers.JSONParser)   

    def put(self , request ):
        product = Product.objects.last()
        serializer = AddImageProSerializer(data=request.data , instance = product)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)

Model

# model

class Product(models.Model):
    name = models.CharField(max_length=200,null=True)
    price = models.FloatField(null=True)
    digital = models.BooleanField(default=False,null=True,blank=False)
    image = models.ImageField(upload_to='images/',null=True,blank=True)

    def __str__(self):
        return self.name

Serializers

# serializers

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['id' , 'name', 'price']


class AddImageProSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['image']

URLs

# urls

urlpatterns = [
    path('', views.ProductView.as_view(), name='ProductView'),
    path('cart', views.OrderItemView.as_view(), name='OrderItemView'),
    path('orders', views.OrderView.as_view(), name='OrderView'),
    path('add', views.AddProduct.as_view(), name='Add'),
    path('addimage', views.AddProductImage.as_view(), name='Addimage'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

问题根源及修复方案

核心问题

你用FileReader.readAsDataURL()把图片转成了base64字符串,但Django的ImageField要求接收原始File对象,而不是编码后的字符串,这直接导致了400参数错误。

分步修复

  1. React端修改图片处理逻辑:
    去掉FileReader,直接保存用户选择的原始文件对象:

    const handleImageChange = (event) => {
      const file = event.target.files[0];
      setFiles(file); // 直接存储File对象,无需转base64
    }
    

    这一步修改后,formDataObj2.append('image', files)就能正确传递文件给Django。

  2. 修复高并发场景下的产品匹配问题:
    当前Django用Product.objects.last()获取产品,在多用户同时操作时会拿到错误的产品。优化方案:

    • 修改AddProduct视图,返回刚创建的产品ID:
      def post(self, request, format=None):
          serializer = self.get_serializer(data=request.data)
          serializer.is_valid(raise_exception=True)
          product = serializer.save()
          # 返回产品ID,供后续图片上传使用
          return Response({'product_id': product.id}, status=status.HTTP_201_CREATED)
      
    • React端在第一个请求成功后,拿到产品ID并传入第二个请求的URL:
      fetch('http://127.0.0.1:8000/add', { method: "POST", body: formDataObj1 })
        .then(response => response.json())
        .then(data => {
          const productId = data.product_id;
          fetch(`http://127.0.0.1:8000/addimage/${productId}`, {
            method: "PUT",
            body: formDataObj2,
          })
          // 后续处理逻辑...
        })
      
    • 修改Django的AddProductImage视图和URL,通过ID获取产品:
      # views.py
      def put(self, request, product_id):
          product = get_object_or_404(Product, id=product_id)
          serializer = AddImageProSerializer(product, data=request.data)
          serializer.is_valid(raise_exception=True)
          serializer.save()
          return Response(serializer.data, status=status.HTTP_200_OK)
      
      # urls.py
      path('addimage/<int:product_id>', views.AddProductImage.as_view(), name='Addimage'),
      
  3. 确认跨域配置:
    如果浏览器控制台有跨域相关错误,安装并配置django-cors-headers,允许React所在域名的请求。

内容的提问来源于stack exchange,提问作者Aws Dayoub

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:57:04