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

Django博客Tinymce上传S3图片预签名URL过期致访问拒绝问题

问题分析

当前问题的核心在于:通过Tinymce上传图片时,后端返回的是临时预签名URL,并将这个URL直接存入了文章内容。预签名URL存在过期时间,一旦过期就会触发AccessDenied错误,而文章内容不会自动更新这个URL,导致图片永久失效。其他图片正常是因为它们的URL生成逻辑不同(比如每次渲染都会重新生成签名,或者本身是公共永久URL)。

解决方案

1. 持久化存储S3对象键,而非预签名URL

预签名URL不适合持久化存储,我们应该存储图片在S3中的唯一键(相对路径),在需要展示时再动态生成最新的预签名URL。

修改views.py中的upload_image视图:

def upload_image(request):
    if request.method == 'POST':
        image = request.FILES.get('image')

        # 修复文件名格式:补充分隔符,确保扩展名正常
        ext = image.name.split('.')[-1]
        filename = f"{uuid.uuid4().hex}.{ext}"

        # 保存图片到S3,获取对象键
        storage = TinymceS3Storage()
        image_key = storage.save(filename, image)

        # 返回S3对象键,而非预签名URL
        return JsonResponse({'file_path': image_key})

    else:
        return JsonResponse({'error': 'Invalid request method'})

2. 渲染文章时动态生成预签名URL

在文章详情视图中,遍历内容里的图片标签,将存储的S3键替换为最新生成的预签名URL:

from django.shortcuts import get_object_or_404, render
import re

def article_detail(request, article_id):
    article = get_object_or_404(Article, id=article_id)
    content = article.content

    # 正则匹配所有图片标签,提取S3对象键
    img_pattern = re.compile(r'<img[^>]+src="([^"]+)"')

    def replace_with_presigned_url(match):
        s3_key = match.group(1)
        storage = TinymceS3Storage()
        # 生成最新的预签名URL
        new_url = storage.url(s3_key)
        return f'<img src="{new_url}"'

    # 替换所有图片URL为最新签名URL
    updated_content = img_pattern.sub(replace_with_presigned_url, content)
    
    return render(request, 'article_detail.html', {'article': article, 'content': updated_content})

在模板中使用{{ content|safe }}渲染处理后的内容。

3. 可选:设置图片为公共可读(无需预签名)

如果博客图片不需要私有访问,可以修改存储类的ACL为公共可读,直接使用永久URL:

class TinymceS3Storage(S3Boto3Storage):
    location = 'media/uploads/tinymce/images'
    file_overwrite = False
    default_acl = 'public-read'  # 设置公共可读

    def get_default_headers(self, *args, **kwargs):
        headers = super().get_default_headers(*args, **kwargs)
        headers['Cache-Control'] = 'public, max-age=31536000'
        return headers

此时storage.url(image_key)会返回永久公共URL,上传时直接返回这个URL存入文章即可,无需后续替换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:57:30