You need to enable JavaScript to run this app. Django实现文件上传与S3存储时出现FieldFile转Unicode及路径不存在报错
优惠活动
大模型
产品
解决方案
定价
更多

Django实现文件上传与S3存储时出现FieldFile转Unicode及路径不存在报错

Django对接AWS S3文件上传报错排查与修复

报错场景

现有代码运行时触发两类异常:

  1. coercing to Unicode: need string or buffer, FieldFile found
  2. [Errno 2] No such file or directory

问题根源定位

  • 第一类Unicode强转报错根源:
    视图层手动执行document.document = request.FILES["document"].read(),直接将上传文件的二进制内容赋值给FileField字段。FileField设计上需要持有上传文件的引用对象,而非原始字节内容,后续Django内部调用存储后端读取文件路径、拼接保存地址时,拿到的是字节串而非正常的FieldFile实例,触发Unicode强转逻辑报错。
  • 第二类文件不存在报错根源:
    代码中file = document.document拿到的是之前读取的二进制文件内容,后续直接将该内容传给open(file, "rb")——open方法仅接收本地文件路径字符串作为入参,传入字节内容既不匹配路径格式,也对应不上任何本地文件,直接触发文件找不到的错误。同时手动调用upload_fileobj时,错误将二进制内容作为S3的object key传入,参数逻辑完全错位。
  • 其他逻辑缺陷:
    上传路径函数中定义了临时变量x但未实际使用,返回值重新拼接了一遍相同的路径,存在冗余代码;视图层上下文传值时,other_documents键名后多了冗余空格,other_documents_id传入的是整个模型实例而非实际的id值;上传路径逻辑依赖instance.created_by_id,但UploadDocument模型未定义对应字段,后续触发上传回调时会报属性不存在的错误;代码中创建了s3_client实例,但实际调用上传方法时直接使用未定义的s3对象,运行时还会触发NameError。

修复方案

  • 调整FileField赋值逻辑:无需手动读取request.FILES中的文件为字节流,直接将上传文件的临时对象赋值给字段即可,避免破坏Django FileField的实例结构
  • 简化S3对接逻辑:如果已通过django-storages配置S3存储后端,无需手动编写boto3上传流程,只要正确赋值FileField,调用save()时Django会自动完成文件上传、路径生成、元数据存储的全流程
  • 补全模型依赖:在UploadDocument模型中补充上传路径回调依赖的created_by字段,避免触发属性访问错误
  • 修正手动上传的参数逻辑:如果必须保留手动S3上传流程,需将自定义规则生成的路径作为object key传入,直接将上传文件的流对象传给upload_fileobj,禁止将文件内容当作本地路径调用open,同时修正未定义变量的引用错误

修复后参考代码

# models.py
import re
import random
import string
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

def document_file_name(instance, filename):
    char_set = string.ascii_uppercase + string.digits
    dat = "".join(random.sample(char_set * 6, 12))
    safe_filename = re.sub(r"[^a-zA-Z0-9-_.]", r"_", filename)
    # 统一拼接路径,删除冗余的重复拼接逻辑
    return f"documents/{instance.created_by_id}/{dat}_{safe_filename}"

class UploadDocument(models.Model):
    # 补充路径回调依赖的created_by字段
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="uploaded_docs")
    document = models.FileField(upload_to=document_file_name, blank=True, null=True)
    other_doc = models.ForeignKey("doc.OtherDocuments", on_delete=models.CASCADE)

# views.py
import boto3
from django.conf import settings
from django.shortcuts import get_object_or_404
from .models import UploadDocument, OtherDocuments

def opportunity_document_add(request, opportunity_id):
    c = setup_context(request)
    other_documents = get_object_or_404(OtherDocuments, pk=opportunity_id)
    # 修正键名多余空格、id传值错误的问题
    c["other_documents"] = other_documents
    c["other_documents_id"] = other_documents.pk
    document = UploadDocument(
        other_doc=other_documents,
        created_by=request.user
    )
    errors = {}
    if request.method == "POST":
        document.type = request.POST.get("type")
        if "document" in request.FILES:
            # 直接赋值上传文件对象,避免手动读取为字节破坏字段结构
            document.document = request.FILES["document"]
            document.save()

注:如果不使用django-storages自动管理S3上传,手动对接时的正确参数逻辑参考:

s3_client = boto3.client("s3")
upload_file = request.FILES["document"]
object_name = document_file_name(document, upload_file.name)
bucket = settings.AWS_STORAGE_BUCKET_NAME
# 直接将上传文件的流对象传给upload_fileobj,不需要open本地文件
s3_client.upload_fileobj(upload_file, bucket, object_name)
document.document = object_name
document.save()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.12 01:45:11