Upload files by python requests

Keywords: Programming Django Python encoding

Reference resources

django upload file

Temporary file, name can be obtained Memory file

demand

django receives the file uploaded by the front end and forwards it to wechat server

requsts upload file

https://2.python-requests.org//zh_CN/latest/user/advanced.html#streaming-uploads

Streaming upload

with open('massive-body') as f:
    requests.post('http://some.url/streamed', data=f)

Block encoding request

def gen():
    yield 'hi'
    yield 'there'

requests.post('http://some.url/chunked', data=gen())

POST multiple block encoded files

>>> url = 'http://httpbin.org/post'
>>> multiple_files = [
        ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
        ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
  ...
  'files': {'images': 'data:image/png;base64,iVBORw ....'}
  'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
  ...
}

requests.models.py

Request code mix in

class RequestEncodingMixin(object):
    @staticmethod
    def _encode_files(files, data):
        """Build the body for a multipart/form-data request.

        Will successfully encode files when passed as a dict or a list of
        tuples. Order is retained if data is a list of tuples but arbitrary
        if parameters are supplied as a dict.
        The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) # file format
        or 4-tuples (filename, fileobj, contentype, custom_headers).
        """
        if (not files):
            raise ValueError("Files must be provided.")
        elif isinstance(data, basestring):
            raise ValueError("Data must not be a string.")

        new_fields = []
        fields = to_key_val_list(data or {})
        files = to_key_val_list(files or {})

        for field, val in fields:
            if isinstance(val, basestring) or not hasattr(val, '__iter__'):
                val = [val]
            for v in val:
                if v is not None:
                    # Don't call str() on bytestrings: in Py3 it all goes wrong.
                    if not isinstance(v, bytes):
                        v = str(v)

                    new_fields.append(
                        (field.decode('utf-8') if isinstance(field, bytes) else field,
                         v.encode('utf-8') if isinstance(v, str) else v))
		# Traverse files to get data in 2 / 3 tuple / list format
        for (k, v) in files:
            # support for explicit filename
            ft = None
            fh = None
            if isinstance(v, (tuple, list)):
                if len(v) == 2:
                    fn, fp = v
                elif len(v) == 3:
                    fn, fp, ft = v
                else:
                    fn, fp, ft, fh = v
            else:
                fn = guess_filename(v) or k
                fp = v

            if isinstance(fp, (str, bytes, bytearray)): # Memory data
                fdata = fp
            elif hasattr(fp, 'read'): # File object, support read
                fdata = fp.read()
            elif fp is None:
                continue
            else:
                fdata = fp

            rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
            rf.make_multipart(content_type=ft)
            new_fields.append(rf)

        body, content_type = encode_multipart_formdata(new_fields)

        return body, content_type

Upload files in django

settings.py

# Set upload file as temporary file to avoid using memory file
FILE_UPLOAD_HANDLERS = [
    'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]

serializers.py upload file fields

class MaterialCreateSerializer(serializers.ModelSerializer):
    name = serializers.CharField(
        required=False, max_length=30, help_text=u'Title')
    content = serializers.FileField(
        required=True, help_text=u'Material content',
        validators=[
            FileExtensionValidator(
                ['png', 'jpg', 'jpeg', 'mp4', 'mp3']
            )
        ]
    )

views.py


class XxxView(generics.GenericAPIView):
    permission_classes = ()
    authentication_classes = ()
    serializer_class = MaterialCreateSerializer

    def post(self, request, *args, **kwargs):
        """
        //Service number of scenic spot in the book of songs

        ---
        parameters:
        - name: object
          pytype: serializers.MaterialCreateSerializer
          paramType: body
        """
        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid():
            logger.error(
                'WXApiView serializer err:{}'.format(serializer.errors))
            return Response(
                serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        data = serializer.validated_data
        url = data.get('url')
        file_data = data.get('image')
        if file_data.closed:
            fp = open(file_data.file.name, 'r') # Never execute
        elif isinstance(file_data, TemporaryUploadedFile):
            fp = file_data.file.file # file object
	   # Use multi file upload in requests, or use but file
        multiple_files = [
            ('file', ('file', fp, file_data.content_type)),
        ]
        resp = _post(url=url, files=multiple_files)
        if not fp.closed:
            fp.close()
        return resp

note

  • After calling fp.read in the requests method, fp.close will not be called.
  • After django orm reads the data, it will shut down actively, and fp.read - > fp.close will also be stored in oss by default.

Posted by Daisatske on Thu, 05 Dec 2019 07:14:36 -0800