"The file cannot be reopened." 移动文件后 django-imagekit 出错

"The file cannot be reopened." error from django-imagekit after moving a file

我正在使用 django-imagekit 在 Django 模型上生成缩略图:

class Book(models.Model):
    title = models.CharField(null=False, blank=False, max_length=255)
    thumbnail = models.ImageField(
        upload_to=upload_path, null=False, blank=True, default=""
    )

    list_thumbnail = ImageSpecField(processors=[ResizeToFit(80, 160)],
                                    source="thumbnail",
                                    format="JPEG")

效果很好。但是,我试图在上传后移动原始 thumbnail 文件。这是我的 save() 方法的简化版本,它只是将文件移动到“新”目录并重新保存对象 ():

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if self.thumbnail and "/new/" not in self.thumbnail.path:
            # Move the thumbnail to correct location.
            initial_name = self.thumbnail.name
            initial_path = self.thumbnail.path
            new_name = os.path.join(os.path.dirname(initial_name),
                                    "new",
                                    os.path.basename(initial_name))
            new_path = os.path.join(settings.MEDIA_ROOT, new_name)

            if not os.path.exists(os.path.dirname(new_path)):
                os.makedirs(os.path.dirname(new_path))

            os.rename(initial_path, new_path)

            self.thumbnail.name = new_name
            kwargs["force_insert"] = False
            super().save(*args, **kwargs)

这在默认情况下工作正常。

但是如果我在 settings.py 中有这个:

IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "imagekit.cachefiles.strategies.Optimistic"

然后我收到由 imagekit 信号引起的错误,大概是在尝试生成 list_thumbnail 时混淆了文件已移动。这是一些追溯:

...
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 33, in receiver
    fn(self, sender=sender, **kwargs)
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 101, in post_save_receiver
    self.dispatch_signal(source_saved, file, sender, instance,
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 124, in dispatch_signal
    signal.send(sender=source_group, source=file)
  File "/venv-path/python3.8/site-packages/django/dispatch/dispatcher.py", line 180, in send
    return [
  File "/venv-path/python3.8/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/venv-path/python3.8/site-packages/imagekit/registry.py", line 116, in source_group_receiver
    call_strategy_method(file, callback_name)
  File "/venv-path/python3.8/site-packages/imagekit/utils.py", line 166, in call_strategy_method
    fn(file)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/strategies.py", line 30, in on_source_saved
    file.generate()
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/__init__.py", line 94, in generate
    self.cachefile_backend.generate(self, force)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/backends.py", line 109, in generate
    self.generate_now(file, force=force)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/backends.py", line 96, in generate_now
    file._generate()
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/__init__.py", line 98, in _generate
    content = generate(self.generator)
  File "/venv-path/python3.8/site-packages/imagekit/utils.py", line 152, in generate
    content = generator.generate()
  File "/venv-path/python3.8/site-packages/imagekit/specs/__init__.py", line 153, in generate
    self.source.open()
  File "/venv-path/python3.8/site-packages/django/db/models/fields/files.py", line 77, in open
    self.file.open(mode)
  File "/venv-path/python3.8/site-packages/django/core/files/base.py", line 114, in open
    raise ValueError("The file cannot be reopened.")
ValueError: The file cannot be reopened.

我可以更改缓存文件策略,但我想保留它。所以我不确定如何告诉 imagekit 字段关于文件的新位置。

事实证明,除了更新 ImageFieldname 属性外,我还必须更新其 File 对象的 path.

所以我添加了这个:

            self.thumbnail.file.path = new_path

保存方法:

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if self.thumbnail and "/new/" not in self.thumbnail.path:
            # Move the thumbnail to correct location.
            initial_name = self.thumbnail.name
            initial_path = self.thumbnail.path
            new_name = os.path.join(os.path.dirname(initial_name),
                                    "new",
                                    os.path.basename(initial_name))
            new_path = os.path.join(settings.MEDIA_ROOT, new_name)

            if not os.path.exists(os.path.dirname(new_path)):
                os.makedirs(os.path.dirname(new_path))

            os.rename(initial_path, new_path)

            self.thumbnail.name = new_name
            self.thumbnail.file.path = new_path  # THE NEW LINE

            kwargs["force_insert"] = False
            super().save(*args, **kwargs)