Django Rest Framework构建的GET API图片URL访问404错误,本地与Heroku环境均无法显示图片
我看了你遇到的问题——用Django Rest Framework做的GET API能返回图片URL,但不管是直接点击URL还是React前端都没法正常显示图片,本地和Heroku线上都有这情况。咱们一步步来排查解决:
1. 先搞定MEDIA路径的配置冲突
你现在的模型里image字段的upload_to='media/pictures',而settings里MEDIA_ROOT = os.path.join(BASE_DIR,'media/pictures'),这会导致图片实际存储路径变成BASE_DIR/media/pictures/media/pictures/xxx.jpg,和你预期的完全不符!
模型里的upload_to是相对于MEDIA_ROOT的子路径,所以得把这两个配置统一:
- 修改模型代码:
class Menus(models.Model): # 其他字段不变 image = models.ImageField(upload_to='pictures', null=True) # 去掉前面的media/ - 修改settings.py里的MEDIA_ROOT:
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 只到media目录,子目录由upload_to控制
这样图片会被存在BASE_DIR/media/pictures/xxx.jpg,对应的访问URL就是/media/pictures/xxx.jpg,路径就对了。
2. 修复生产环境的MEDIA路由
你现在的项目URL配置只在DEBUG=True时才加media路由,但Heroku线上环境默认DEBUG=False,所以media文件根本没被服务起来,自然访问不了。
修改项目的urls.py,确保生产环境也能处理media请求:
from django.conf import settings from django.conf.urls.static import static from django.views.static import serve urlpatterns = [ # 你的现有路由,比如admin、api这些 path('admin/', admin.site.urls), path('api/menus/', include('your_app_name.urls')), ] # 处理media文件的路由,兼容本地和生产环境 if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) else: # 生产环境用serve视图处理media请求 urlpatterns += [ path(settings.MEDIA_URL.lstrip('/') + '<path:path>', serve, {'document_root': settings.MEDIA_ROOT}), ]
3. 解决Heroku的临时文件系统问题
Heroku的文件系统是临时的,每次部署、重启后,你上传的本地media文件都会丢失,而且上传的图片也不会被Git跟踪(应该在.gitignore里排除了media目录)。所以生产环境必须用云存储来存图片,比如AWS S3或者Cloudinary。
举个Cloudinary的快速配置例子:
- 安装依赖:
pip install cloudinary django-cloudinary-storage - 在settings.py里添加配置:
INSTALLED_APPS += ['cloudinary_storage', 'cloudinary'] # 替换成你自己的Cloudinary账号信息 CLOUDINARY_STORAGE = { 'CLOUD_NAME': '你的Cloudinary云名称', 'API_KEY': '你的API密钥', 'API_SECRET': '你的API密钥密码' } DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
这样上传的图片会自动存在Cloudinary,API返回的URL是Cloudinary的公共地址,不管Heroku怎么重启都不会丢,而且访问速度更快。
4. 确保序列化器生成正确的绝对URL
你写的image_url字段依赖request上下文来生成绝对URL,要确保视图里把request传给序列化器:
from rest_framework import generics from .models import Menus from .serializers import MenusSerializer class MenusListView(generics.ListAPIView): queryset = Menus.objects.all() serializer_class = MenusSerializer def get_serializer_context(self): # 把request传入序列化器上下文 context = super().get_serializer_context() context['request'] = self.request return context
这样request.build_absolute_uri(image_url)才能生成完整的http/https绝对URL,React前端也能直接用这个URL加载图片。
按上面的步骤改完后,不管本地还是Heroku线上,图片URL应该都能正常访问,React前端也能顺利展示图片了。
内容的提问来源于stack exchange,提问作者Reactoo

