I am using python requests to put file to django rest api:
put:
def pretty_print(req):
print('1,{}\n2,{}\n3,{}\n\n4,{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body[0:10],
))
print "----------END------------"
try:
req = requests.Request('PUT',
'https://myip/myproject/api/upload',
headers={'content-type':'multipart/form-data'},
files={"file": ('file1', open(filepath, "rb"))} )
prepped = req.prepare()
print "prepped:", prepped.headers
#print "prepped:", prepped.body
pretty_print( prepped )
s = requests.Session()
r = s.send(prepped, verify=False)
print "response:", r.json()
except Exception as e:
print "Exception:", e
views.py
class FileUploadView(APIView):
parser_classes = (MultiPartParser,)
def put(self, request):
try:
print "data:", request.data.dict()
print "file:", request.FILES.dict()
print "----------"
data = {'files': 'testing'}
response = Response(data)
But, I got the empty dicts.
data: {}
file: {}
And the print out of the requests:
prepped: {'Content-Length': '18364', 'content-type': 'text/plain'}
1,-----------START-----------
2,PUT https://10.32.64.4/reporting-central/api/upload
3,content-type: text/plain
Content-Length: 18364
4,--e9541743
----------END------------
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:318: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.
SNIMissingWarning
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
InsecurePlatformWarning
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py:821: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.org/en/latest/security.html
InsecureRequestWarning)
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py:821: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.org/en/latest/security.html
InsecureRequestWarning)
response: {u'files': u'testing'}
Anything wrong? Any comments welcomed. Thanks.
UPDATE
my app url.py:
from api.views import *
urlpatterns = [
url(r'^upload/$', FileUploadView.as_view(), name='api-upload'),
url(r'^test/$', test, name='api-test'),
]
my project url.py:
urlpatterns = patterns('',
url(r'^api/', include('api.urls')),
)
UPDATE2
I found that maybe my requests PUT is wrong. because I used postman to do put, which was successful. Still hope to figure out why my requests PUT not work.
Answer
The issue with your code lies in how the requests
library handles the PUT
request with a file upload. Specifically:
-
Content-Type Misconfiguration: When uploading files, the
Content-Type
should be set tomultipart/form-data
. However, in your code, it's being overridden astext/plain
in theheaders
dictionary, which causes the Django server to fail to parse the uploaded file properly. -
Incorrect
Request
Object Usage: You're usingrequests.Request
and preparing it manually, butfiles
is handled better directly inrequests.put
orrequests.post
. -
Empty
FILES
anddata
Dicts: This occurs because the request isn't formatted as a proper multipart/form-data request.
Fixing the Code
Here’s the corrected code for uploading a file with a PUT
request:
Client-Side (Python Requests)
import requests
def pretty_print(req):
print('1, {}\n2, {}\n3, {}\n\n4, {}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body[:100], # Print first 100 characters of the body for brevity
))
print("----------END------------")
filepath = "path/to/your/file.txt"
try:
with open(filepath, "rb") as f:
# Using requests.put directly
files = {"file": ("file1.txt", f)}
headers = {"Accept": "application/json"} # No need to specify content-type manually
url = "https://myip/myproject/api/upload"
response = requests.put(url, files=files, headers=headers, verify=False)
# Pretty print request and response
pretty_print(response.request)
print("Response:", response.status_code, response.json())
except Exception as e:
print("Exception:", e)
Server-Side (Django)
Ensure your views.py
is correctly set up to handle file uploads. The FileUploadView
appears fine, but here’s a double-check:
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
class FileUploadView(APIView):
parser_classes = (MultiPartParser,) # Ensure MultiPartParser is used
def put(self, request, *args, **kwargs):
file_obj = request.FILES.get('file') # Access uploaded file
if file_obj:
print("Received file:", file_obj.name)
return Response({"message": "File received successfully", "file_name": file_obj.name})
return Response({"error": "No file uploaded"}, status=400)
Key Points
- Use
files
Argument: Inrequests.put
, always use thefiles
argument for file uploads, and letrequests
handle theContent-Type
header. - Correct File Access: Use
request.FILES.get('file')
on the Django side to retrieve the uploaded file. - Validation: Add checks to ensure the file is present and handle errors gracefully.
- Testing: Use a tool like Postman or
curl
to validate your API endpoints independently of the client-side code.
Example curl
Test
curl -X PUT -F "file=@path/to/your/file.txt" https://myip/myproject/api/upload
If this works, it confirms that the issue lies with your Python client code. The fixed version provided above should resolve the problem.