Django - 大文件上传 - 将文件从临时文件夹移动到最终目的地失败

问题描述 投票:0回答:1

在将 Python、Django 和 Alpine 升级到最新版本时偶然发现了这个错误......这是一个奇怪的错误。 当我上传大于 FILE_UPLOAD_MAX_MEMORY_SIZE(设置为 5MB)的文件时,会发生错误。

我设置了两个开发环境。

  • 一个是本地开发服务器(只是普通的 py manage.py runserver),
  • 另一个运行在 Docker(Windows) + Alpine linux + Apache2 + mod_wsgi 下,这仍然是一个“开发”环境。

此错误仅在 Docker 环境下且仅当文件超过最大内存大小时发生。在所有其他情况下,上传都可以工作 - 即使文件超出本地开发服务器上的内存限制。

错误:

Saving new order file to: customer_name/_web_portal/PendingOrders/7935/zip_03.zip 

Internal Server Error: /b2b_api/orders/
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/api/views.py", line 352, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/rest_framework/mixins.py", line 19, in create
    self.perform_create(serializer)
  File "/code/api/views.py", line 375, in perform_create
    serializer.save()
  File "/usr/local/lib/python3.11/site-packages/rest_framework/serializers.py", line 212, in save
    self.instance = self.create(validated_data)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/api/serializers.py", line 286, in create
    instance = super().create(validated_data)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/api/serializers.py", line 125, in create
    db_file.save()
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save
    self.save_base(
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table
    results = self._do_insert(
              ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert
    return manager._insert(
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1821, in execute_sql
    for sql, params in self.as_sql():
                       ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1745, in as_sql
    value_rows = [
                 ^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1746, in <listcomp>
    [
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1747, in <listcomp>
    self.prepare_value(field, self.pre_save_val(field, obj))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1695, in pre_save_val
    return field.pre_save(obj, add=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/models/fields/files.py", line 317, in pre_save
    file.save(file.name, file.file, save=False)
  File "/usr/local/lib/python3.11/site-packages/django/db/models/fields/files.py", line 93, in save
    self.name = self.storage.save(name, content, max_length=self.field.max_length)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/files/storage/base.py", line 38, in save
    name = self._save(name, content)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/files/storage/filesystem.py", line 130, in _save
    os.chmod(full_path, self.file_permissions_mode)
FileNotFoundError: [Errno 2] No such file or directory: '/sftp/customer_name/_web_portal/PendingOrders/7935/zip_03.zip'

奇怪的是,当我查看文件夹时,文件就在那里。 Apache 运行的用户可以访问它。

尝试检查

inotifywait
发生了什么,这是上传目录的输出:

Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
/sftp/customer_name/_web_portal/PendingOrders/ CREATE,ISDIR 7982
/sftp/customer_name_web_portal/PendingOrders/ OPEN,ISDIR 7982
/sftp/customer_name/_web_portal/PendingOrders/ ACCESS,ISDIR 7982
/sftp/customer_name/_web_portal/PendingOrders/ CLOSE_NOWRITE,CLOSE,ISDIR 7982
# --> between those two records the error is thrown
/sftp/customer_name/_web_portal/PendingOrders/7982/ CLOSE_WRITE,CLOSE zip_02.zip

还尝试在错误发生之前编辑 django 文件(就在

os.chmod
...之前)并打印文件是否存在,这是输出:

os.listdir(directory)=['zip_10MB.zip']
os.path.exists(full_path)=False
os.path.isfile(full_path)=False
repr(full_path)="'/sftp/customer_name/_web_portal/PendingOrders/8013/zip_10MB.zip'" repr(directory)="'/sftp/customer_name/_web_portal/PendingOrders/8013'"

这对我来说是不真实的,因为 listdir 输出该文件夹中有一个文件,但

os.path.exists
输出 False?! 我发现
os.path.exists()
isfile
在下面调用
os.stat()
来查找必要的信息,...所以我尝试了
os.stat(full_path)
但失败并出现 FileNotFoundError。

抛出错误后,我使用运行 Apache 的同一用户登录 Docker,并尝试在所述文件路径上运行

os.stat()
,它返回 OK(文件信息)。

我还检查了 Django 将文件从临时目录移动到最终目的地时使用的操作 - 它是 django/core/files/move.py file_move_safe 函数 - 而且它是一个简单的

os.rename()

我尝试的下一步是将

time.sleep()
放在实际错误之前,并将
cd
-ing 放在上传目录中,并对每个用户执行
ls
命令,这是输出: 根用户:

total 10432
drwxrwx---    1 web_port web_port       512 Aug 17 09:48 .
drwxrwx---    1 web_port web_port       512 Aug 17 09:48 ..
-rw-------    1 web_port web_port  10679630 Aug 17 09:48 zip_10MB.zip

web_portal(apache / 应用程序)用户:

ls: ./zip_10MB.zip: No such file or directory
total 0
drwxrwx---    1 web_port web_port       512 Aug 17 09:48 .
drwxrwx---    1 web_port web_port       512 Aug 17 09:48 ..

Ps:我正在使用

python:3.11-alpine
Docker 镜像(Docker Desktop),以及
Django==4.2.4
djangorestframework==3.14.0

更新: 尝试将我的 Docker 映像/容器放置在 Linux 操作系统上 - 文件上传工作没有问题。所以我猜测 Windows 版 Docker 出了问题,或者 Windows 上处理“绑定”挂载的方式出了问题。也许?

更新#2: 知道文件是否被 Windows Defender 或其他东西锁定。我不知道我已经没有主意了。所以我禁用了 Windows Defender,在文件夹上添加了例外,..错误仍然存在。接下来,我尝试在文件夹上启用 Windows 审核日志记录,但我不知道如何从安全日志的输出中理解。

有谁知道问题可能是什么,或者我接下来可以尝试什么来进一步限制此问题的原因?

django linux docker apache alpine-linux
1个回答
0
投票

尝试在您的

MAX_UPLOADING_FILE_SIZE
文件中设置
settings.py
,此解决方案可以帮助我完成我的项目:

MAX_UPLOADING_FILE_SIZE = 31457280  # 30 MB
© www.soinside.com 2019 - 2024. All rights reserved.