使用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参数错误。
分步修复
React端修改图片处理逻辑:
去掉FileReader,直接保存用户选择的原始文件对象:const handleImageChange = (event) => { const file = event.target.files[0]; setFiles(file); // 直接存储File对象,无需转base64 }这一步修改后,
formDataObj2.append('image', files)就能正确传递文件给Django。修复高并发场景下的产品匹配问题:
当前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'),
- 修改
确认跨域配置:
如果浏览器控制台有跨域相关错误,安装并配置django-cors-headers,允许React所在域名的请求。
内容的提问来源于stack exchange,提问作者Aws Dayoub
相关产品推荐
相关产品推荐

