由DRF上传图片温习到的HTTP知识

在前后端开发的时候,Djangorestframework(简称DRF)是个及其强大的附属于Django的第三方库,依赖于Model的CURD操作大大减少了代码量。之前使用DRF的时候基本都是跟前端json交互,非常容易,但是最近要上传以及跟新图片,遇到了一些小问题,期间不知不觉又拓展到了Http的知识,发现自己有在这方面有些薄弱,温习了一波,特在此记录一波。

1. 熟悉而又陌生的HTTP

某大佬云:学习Web开发不好好学习HTTP报文,将会“打拳不练功,到老一场空”,你花在犯迷糊上的时间比你沉下心来学习HTTP的时间肯定会多很多。

这句话在我看来是及其正确的。作为一个后端开发,不仅仅在于空荡荡地写代码,单身三十年的手速来辅助粘贴,更重要的是在代码有问题进行调试时,了解代码在框架中的运行流程。心中对整个过程了如指掌的程序猿解决问题自然很快,反之,则会花大把时间犯糊涂。

刚实习的那会,大把大把的时间,看过一本思科网络技术学院教程,我是在当当买的,但是听同事说是大学教科书,囧,不过内容古镇的是不错,当时粗粗一读已经解决了大部分心中的疑惑,上网是怎样的过程,TCP/IP啥的。不过还是对细节掌握太少,不然也不会有今日的困惑,乃至回头要重新翻这本书。

教程这里就不说了,网上大把大把,图文并茂,生动地很,这里这是给出链接,自己整理下。

一张是TCP/IP模型

TCP/IP

另外一张是HTTP协议栈中各层数据流

HTTP协议栈中各层数据流

为什么要贴出来,重要程度就不说了。

2. 让我从DRF瞬移到了HTTP的问题

假设有这样一个Models,

1
2
3
class BeautifulGirl(models.Model):
name = models.CharField(max_length=32, null=True, unique=True)
img = models.ImageField(null=True, blank=True, upload_to='beauty/')

我View视图先按照传统写的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 上传美女图片
class UploadBeauty2(APIView):
# parser_classes = (MultiPartParser,)

def post(self, request):
data = request.data
beauty_name = data['name']
if beauty_name in BeautifulGirl.objects.values_list('name', flat=True):
return Response({'msg': 'repeat name'}, status=500)
image = data['image']
img_name = image.name
girl = BeautifulGirl()
girl.name = beauty_name
girl.img.save(img_name, image, save=False)
girl.save()
return Response({'msg': 'upload ok'}, status=200)

刚开始没有**parser_classes = (MultiPartParser,)这一行,而且我的DRF parser 只设置成了'rest_framework.parsers.JSONParser',,好了找到问题,那我看看哪个合适,FileUploadParser看上去合适的,设置一下,报错Missing filename. Request should include a Content-Disposition header ....**
后来看到了解决方法,OK了,但是我对这个**MultiPartParser很懵逼,FileUploadParser**不就是上传图片,为何不可行。

其实遇到这个问题也是我设置的问题,DRF默认解析包好了这个MultiPartParser,但我觉得平常只用json,所以只设置成了JSONParser,尴尬,用默认设置即可

3. 使用Django进行文件上传的基础知识

这节的内容翻译自这里

  • 将文件提交到服务器时,文件数据最终会被放入request.FILES。

  • HTML表单必须使用**enctype=”multipart/form-data”**正确设置属性。否则request.FILES将是空的。

  • 必须使用POST方法提交表单。

  • Django有适当的模型字段来处理上传的文件:FileField和ImageField。

  • 上传到FileField或ImageField不存储在数据库中,存储在文件系统中的文件。

  • FileField和ImageField在数据库中创建为字符串字段(通常为VARCHAR),包含对实际文件的引用。

  • 如果删除包含FileField或ImageField的模型实例,Django将不会删除物理文件,而只会删除对文件的引用。

  • 这request.FILES是一个类字典的对象。request.FILES的键值的名称都是来自<input type="file" name="" />

  • 每个值request.FILES都是一个UploadedFile实例。

  • 你将需要设置MEDIA_URL并MEDIA_ROOT在项目的settings.py。

1
2
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • 在开发服务器中,你可以使用django.contrib.staticfiles.views.serve() 视图为用户上载的文件(媒体)提供服务。
1
2
3
4
5
6
7
8
9
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
# Project url patterns...
]

if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • 要访问MEDIA_URL模板,你必须添加django.template.context_processors.media到您的 context_processeors内部TEMPLATES配置。

4 . multipart/form-data 是什么

整理一下两篇极佳的科普文章:

  1. 四种常见的 POST 提交数据方式
  2. what-does-enctype-multipart-form-data-mean?

5. FileUploadParser是什么以及更简单的方式上传图片

FileUploadParser适用于可以将文件作为原始数据请求上载的本机客户端。对于基于Web的上载或具有分段上传支持的本机客户端,应该使用MultiPartParser

**好了,建议是不应该使用 =。= **

其实更好Crete数据的方法还是用DRF的通用视图,贼方便

1
2
3
4
class UploadBeauty(ListCreateAPIView):
queryset = BeautifulGirl.objects.all()
serializer_class = BeautifulGirlSerializers
parser_classes = (MultiPartParser,FormParser)

6. 回过头看看

既然作为一个稳定的web框架,更多的是让开发者方便快速,不用过多地在意包括http以及更多协议中本身繁杂晦涩的概念与操作,封装地很完美,但是很多东西,比如设置文件里仅仅一行字段就能代表很多东西,预留了默认设置,让初学者更加方便快捷,上手即用,没有门槛。但其中很多通用的基础知识在代码运行流程内部体现,需要掌握并且动手不断地尝试,必要时读源码来了解原由。