I am trying to unit test my file uploading REST API. I found online some code generating the image with Pillow but it can't be serialized.
This is my code for generating the image :
image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
file = BytesIO(image.tobytes())
file.name = 'test.png'
file.seek(0)
Then I try to upload this image fille :
return self.client.post("/api/images/", data=json.dumps({
"image": file,
"item": 1
}), content_type="application/json", format='multipart')
And I get the following error:
<ContentFile: Raw content> is not JSON serializable
How can I transform the Pillow image so it's serializable?
I wouldn't recommend submitting your data as JSON in this case, as it complicates the issue. Just make a POST request with the parameters and files you want to submit. Django REST Framework will handle it just fine without you needing to serialise it as JSON.
I wrote a test for uploading a file to an API endpoint a while back which looked like this:
def test_post_photo(self):
"""
Test trying to add a photo
"""
# Create an album
album = AlbumFactory(owner=self.user)
# Log user in
self.client.login(username=self.user.username, password='password')
# Create image
image = Image.new('RGB', (100, 100))
tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
image.save(tmp_file)
# Send data
with open(tmp_file.name, 'rb') as data:
response = self.client.post(reverse('photo-list'), {'album': 'http://testserver/api/albums/' + album.pk, 'image': data}, format='multipart')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
In this case, I used the tempfile module to store an image generated using Pillow. The with syntax used in the example allows you to pass the content of the file in the request body comparatively easily.
Based on this, something like this should work for your use case:
image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
file = tempfile.NamedTemporaryFile(suffix='.png')
image.save(file)
with open(file.name, 'rb') as data:
return self.client.post("/api/images/", {"image": data, "item": 1}, format='multipart')
Incidentally, depending on your use case it might be more convenient to accept the image data as a base 64 encoded string.
You converted the file to bytes, which is not JSON serializable.
Without knowing what your API expects to receive, I'll have to take a guess that you have to encode file as a string: "image": file.decode('utf-8').
While there are many solutions to your general issue of unit testing image uploads to a REST API
Related
i have post request to my python code. in the json body I need to send 1 parameter. and then I need to upload csv file.
I have 2 questions: 1. how to upload the csv from postman side 2. how to get it in my python code.
attached my post request and my python code.
post request screen shot
my code in python.
#cherrypy.tools.json_in()
#cherrypy.tools.json_out()
#cherrypy.tools.accept(media='application/json')
def POST(self):
body = cherrypy.request.json
If you want to use JSON to upload data. You need to convert csv file in to base64 string before you post your data. Because JSON format does not support file.
If you just want to get your data, you can select "form-data" in postman.
Code for 'form-data':
import cherrypy
#cherrypy.expose
#cherrypy.tools.json_out()
def uploadcsv(self, img=None, other=None):
print(img)
print(other)
return 'ok'
cherrypy.quickstart(HelloWorld())
Image of postman setting:
I followed the steps in media/upload. I wrote this function in python
def upload_media(self,access_token,image_url):
client = self.get_client(access_token)
message = {'media' : image_url}
encoded_status = urllib.urlencode(message)
url = "https://upload.twitter.com/1.1/media/upload.json?"+ encoded_status
resp, content = client.request(url,'post')
return content
And I got this :
{"request":"\/1.1\/media\/upload.json","error":"media type unrecognized."}
As far as I can tell, the error is in trying to upload a URL. The Twitter API requires you to upload a base64-encoded image.
See: https://dev.twitter.com/rest/reference/post/media/upload
So instead of the image's URL, it should be the file content:
with open('example.jpg', 'rb') as f:
data = f.read()
message = {'media':data}
Optionally (I still haven't figured out whether this is required or not, as different people give different answers), you could encode the image in base-64 encoding:
with open('example.jpg', 'rb') as f:
data = f.read()
data = data.encode('base64')
message = {'media':data}
I am using RecorderJS in my django application. In this, multiple audio files are created in the browser. My question is how can I store these files in the server (directly from the browser).
I have got few leads like using upload_to in FileField (which can be stored via forms) or using file-based sessions in Django. However, I am not sure how to proceed as it seems complicated for me to combine the following reasons:
multiple files to be stored simulataneously,
and storing directly from browser (not manually uploading in the browser).
Does anyone have solution to this. Thanks in advance.
[Update]
I have proceeded the direction as shown below in the form of a code:
In urls.py
url(r'^audio_file/$', 'nalign_app_recorder.views.recorder'),
In models.py
class InputFile(models.Model):
audio_file = models.FileField(upload_to='/audio')
input_user = models.ForeignKey(User)
rec_date = models.DateTimeField('date recorded', auto_now_add=True)
I am sending the audio file (blob) via Ajax Jquery.
function uploadBlob(blob) {
var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('data', blob);
$.ajax({
type: 'POST',
url: '/audio_file/',
data: fd,
processData: false,
contentType: false,
success: function(response) {
console.log("everything worked!");
$("#audio_file").html(response);
},
error: function(obj, status, err) { alert(err); console.log(err); }
});
Then I receive this file in the Django view [views.py]. However, though the file is received but the error is generated when the save() method is invoked. Does anyone know the solution for this or is there any better method:
#csrf_exempt
def recorder(request):
if request.method=='POST' or request.is_ajax():
e1=InputFile()
e1.audio_file=request.FILES #<-- Audio file is received here
e1.input_user=request.user
e1.rec_date=datetime.datetime.now()
e1.save() #<-- When save() method is executed, **error** is generated
return HttpResponseRedirect(--another-page--)
return render_to_response('recorder2.html',RequestContext(request))
Due to security reasons browsers don't allow you to set value of <file> form field. (Imagine hiding this field and setting the value to some vulnerable file).
You need to post the file via JavaScript.
I'm trying to upload a file into my web service (written using DJango REST framework). I have written the following code but I get data can not be converted to utf-8 error
with open('/images/img.jpg', 'rb') as imgFile:
content = imgFile.read ()
json = { 'fileName': 'img.jpg', 'img': content}
json_data = simplejson.dumps(json)
reqURL = urllib2.Request("http://localhost:8000/uploadfile/",json_data)
opener = urllib2.build_opener()
f = opener.open(reqURL)
What is the right way of passing file content over JSON?
You don't send files like this. File contents are sent by embedding them inside the request body.
You may be better of by using the beautiful python-request library. Check out the file upload section.
I'm using Valumns File Uploader to load files to django. This supports an ajax upload via XHR for modern browsers and an iFrame fallback for older ones like IE.
Now the problem: If I upload large files, only iFrame upload works, because here the files are mapped into request.FILES and django writes them to the disk immediately instead of memory.
If XHR upload is used, I have to read request._raw_post_data and write this to disk, but it fails with an MemoryError.
This is my file uploader initialization:
var uploader = new qq.FileUploader({
'action': uploadUrl,
'multiple': true,
'allowedExtensions': allowedExtensions,
'element': selector,
'debug': true,
'onComplete': completeFunction,
'onProgress': progressFunction,
'onSubmit': submitFunction
});
At the django end I use the following code to write the file content to disk:
# open a new file to write the contents into
new_file_name = str(uuid.uuid4())
destination = open(upload_path + new_file_name, 'wb+')
# differentiate between xhr (Chrome, FF) and pseudo form uploads (IE)
if len(request.FILES) > 0: # IE
for chunk in request.FILES[0].chunks():
destination.write(chunk)
else: # others
destination.write(request._raw_post_data)
destination.close()
I also tried this solution from Alex Kuhl, but this fails with an IOError: request data read error.
Is there any way to get the files uploaded via XHR into request.FILES and so use the django builtin handling?