file does not have buffer interface - python

i have a function in my views.py. It is like this
from django.core.files.uploadedfile import SimpleUploadedFile
def get_file(self, url):
# pdb.set_trace()
result = urllib.urlretrieve(url)
fi = open(result[0])
fi_name = os.path.basename(url)
suf = SimpleUploadedFile(fi_name, fi)
return suf
while creating SimpleUploadedFile object i get the error saying
TypeError: file doesnot have buffer interface
I tried changing open mode to 'rb'. But still get the same error
Plz help me out

SimpleUploadedFile needs the actual file content, rather than a file object. So you could fix your code like this:
suf = SimpleUploadedFile(fi_name, fi.read())
I must say though I don't know why you are using urlretrieve, which saves the content to a local temp file which you then must open and read. Better to use urlopen and pass it directly:
result = urllib.urlopen(url)
fi_name = os.path.basename(url)
suf = SimpleUploadedFile(fi_name, result.read())

Had this problem on the dreaded El Capitan, in the requests lib.
It seems that passing unicode as HTTP content breaks stuff when converted to memoryview() on the socket layer.
Just passing everything as plain strings fixed it for me.

Related

How to save image which sent via flask send_file

I have this code for server
#app.route('/get', methods=['GET'])
def get():
return send_file("token.jpg", attachment_filename=("token.jpg"), mimetype='image/jpg')
and this code for getting response
r = requests.get(url + '/get')
And i need to save file from response to hard drive. But i cant use r.files. What i need to do in these situation?
Assuming the get request is valid. You can use use Python's built in function open, to open a file in binary mode and write the returned content to disk. Example below.
file_content = requests.get('http://yoururl/get')
save_file = open("sample_image.png", "wb")
save_file.write(file_content.content)
save_file.close()
As you can see, to write the image to disk, we use open, and write the returned content to 'sample_image.png'. Since your server-side code seems to be returning only one file, the example above should work for you.
You can set the stream parameter and extract the filename from the HTTP headers. Then the raw data from the undecoded body can be read and saved chunk by chunk.
import os
import re
import requests
resp = requests.get('http://127.0.0.1:5000/get', stream=True)
name = re.findall('filename=(.+)', resp.headers['Content-Disposition'])[0]
dest = os.path.join(os.path.expanduser('~'), name)
with open(dest, 'wb') as fp:
while True:
chunk = resp.raw.read(1024)
if not chunk: break
fp.write(chunk)

How can i write raw data?

I'm testing some things and i keep getting the error "write() argument must be str, not HTTPResponse" Here's my code:
import requests
image="http://www.casperdenhaan.nl/wp-content/uploads/2020/03/Logo.jpg"
savefile=open("image.png","w+")
savefile.write(requests.get(image).raw)
savefile.close()
I can get the raw data, but I can't write it to a new file. Is there a way i could get around this problem?
When you call .raw on the response object it returns an HTTPResponse object. You need to call .content to get a bytes object.
type(requests.get(image).raw)
urllib3.response.HTTPResponse
type(requests.get(image).content)
bytes
You need to open the file in write binary mode:
open("image.png","wb")
I suggest using a "with" block, this way you don't need to explicitly close the file. Here is a working version of the code:
import requests
url = "http://www.casperdenhaan.nl/wp-content/uploads/2020/03/Logo.jpg"
with open('image.png', 'wb') as f:
f.write(requests.get(url).content)
try this way of doing it
import requests
img_url = "http://www.casperdenhaan.nl/wp-content/uploads/2020/03/Logo.jpg"
img = requests.get(img_url)
with open('image.png', 'wb') as save_file:
save_file.write(img.raw)
This way you don't have to deal with closing the file. Also, the 'wb' opens the file in a writable binary mode.

send_file() when called return text document instead of an image

I want to send an image file from server side to client side. I am using flask framework.
But the problem is whenever I call the route in which send_file() is, the response return is an File. When I click this file gedit opens it with nothing in that file. That means it must be text file written.
I referred the flask docs for send_file().
Here is what I am doing in the code:
#app.route('/try')
def trial():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
return send_file(resp,mimetype="image/jpeg",attachment_filename="img.jpg",as_attachment=True)
Whenever I load localhost:5000/try a file is downloaded but not the image file that I want to download.
Error I am getting in my terminal is AttributeError: 'Response' object has no attribute 'read' error.
What must be the problem. Is anything missing in this above snippet?
resp is a requests.models.Response object, not string nor bytes:
>>> import requests
>>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
>>> resp = requests.get(todown)
>>> resp
<Response [200]>
>>> type(resp)
<class 'requests.models.Response'>
Flask.send_file() sends a file.
So first at all you need use resp.content to get the content of the object, it'll return bytes object (and by the way, resp.text return string object.
Always use .content if you're downloading an image, video, or other non-text things).
>>> import requests
>>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
>>> resp = requests.get(todown)
>>> type(resp.content)
<class 'bytes'>
Please check the document for more details.
Then, because Flask.send_file() send a file, so you need write the image into a file before you send it.
But since you don't need use this image on your server anyway, I'd suggest use io.BytesIO in this case, then you don't need delete that image after you sent it. And note that use io.StringIO if you're sending a text file.
For example:
import requests
from io import BytesIO
from flask import Flask, send_file
app = Flask(__name__)
#app.route('/')
def tria():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
return send_file(BytesIO(resp.content), mimetype="image/jpeg", attachment_filename="img2.jpg", as_attachment=True)
app.run(port=80, debug=True)
However, if you want write the image into a file and send it then, sure you can also do it. We can use tempfile.NamedTemporaryFile() to create a tempfile instead of just create a file to avoid rewrite your important files.
From the document:
This function operates exactly as TemporaryFile() does, except that the file is guaranteed to have a visible name in the file system (on Unix, the directory entry is not unlinked).
That name can be retrieved from the name attribute of the file object. Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later). If delete is true (the default), the file is deleted as soon as it is closed.
The returned object is always a file-like object whose file attribute is the underlying true file object. This file-like object can be used in a with statement, just like a normal file.
For example:
import tempfile
import requests
from flask import Flask, send_file
app = Flask(__name__)
#app.route('/')
def tria():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
with tempfile.NamedTemporaryFile() as f:
# create a file-like object use `NamedTemporaryFile()` and `with`
# as the basic usage which said in the document
f.write(resp.content)
# write the content of the image into it
return send_file(f.name, mimetype="image/jpeg",
attachment_filename="img2.jpg", as_attachment=True)
# `f.name` is the temp file's filename
app.run(port=80, debug=True)

Open a file from urlfetch in GAE

I'm trying to open a file in GAE that was retrieved using urlfetch().
Here's what I have so far:
from google.appengine.api import urlfetch
result = urlfetch.fetch('http://example.com/test.txt')
data = result.content
## f = open(...) <- what goes in here?
This might seem strange but there's a very similar function in the BlobStore that can write data to a blobfile:
f = files.blobstore.create(mime_type='txt', _blobinfo_uploaded_filename='test')
with files.open(f, 'a') as data:
data.write(result.content)
How can I write data into an arbitrary file object?
Edit: Should've been more clear; I'm trying to urlfetch any file and open result.content in a file object. So it might be a .doc instead of a .txt
You can use the StringIO module to emulate a file object using the contents of your string.
from google.appengine.api import urlfetch
from StringIO import StringIO
result = urlfetch.fetch('http://example.com/test.txt')
f = StringIO(result.content)
You can then read() from the f object or use other file object methods like seek(), readline(), etc.
Yoy do not have to open a file. You have received the txt data in data = result.content.

Django "Unable to determine the file's size" error with tempfile.TemporaryFile

I'm having problems with the standard Django FileField and tempfile.TemporaryFile. Whenever I try to save a FileField with the TemporaryFile, I get the "Unable to determine the file's size" error.
For example, given a model named Model, a filefield named FileField, and a temporaryfile named TempFile:
Model.FileField.save('foobar', django.core.files.File(TempFile), save=True)
This will give me the aforementioned error. Any thoughts?
I had this problem with tempfile.TemporaryFile. When I switched to tempfile.NamedTemporaryFile it went away. I believe that TemporaryFile just simulates being a file (on some operating system at least), whereas NamedTemporaryFile really is a file.
I was having the same problem and was able to solve it for my case. This is the code that django uses to determine the size of a file:
def _get_size(self):
if not hasattr(self, '_size'):
if hasattr(self.file, 'size'):
self._size = self.file.size
elif os.path.exists(self.file.name):
self._size = os.path.getsize(self.file.name)
else:
raise AttributeError("Unable to determine the file's size.")
return self._size
Therefore, django will raise an AttributeError if the file does not exist on disk (or have a size attribute already defined). Since the TemporaryFile class attempts to create a file in memory instead of actually on disk, this _get_size method doesn't work. In order to get it to work, I had to do something like this:
import tempfile, os
# Use tempfile.mkstemp, since it will actually create the file on disk.
(temp_filedescriptor, temp_filepath) = tempfile.mkstemp()
# Close the open file using the file descriptor, since file objects
# returned by os.fdopen don't work, either
os.close(temp_filedescriptor)
# Open the file on disk
temp_file = open(temp_filepath, "w+b")
# Do operations on your file here . . .
modelObj.fileField.save("filename.txt", File(temp_file))
temp_file.close()
# Remove the created file from disk.
os.remove(temp_filepath)
Alternatively (and preferably), if you can calculate the size of the temporary file you're creating, you could set a size attribute on the TemporaryFile object directly. Due to the libraries I was using, this was not a possibility for me.
I had this issue on Heroku even with tempfile.NamedTemporaryFile and was quite disappointed ...
I solved it using Steven's tips by setting arbitrary size manually (yes, dirty, but work for me):
from django.core.files import File
from django.core.files.temp import NamedTemporaryFile
img_temp = NamedTemporaryFile()
# Do your stuffs ...
img_temp.flush()
img_temp.size = 1024
media.thumbnail.save('dummy', File(img_temp))
Thanks !
I know this is a bit old but I've managed to save a base64 file (without having the actual file saved on the disk) by using the ContentFile class provided by Django.
According to the docs:
The ContentFile class inherits from File, but unlike File it operates on string content (bytes also supported), rather than an actual file.
The snippet below receives a base64 string, extract it's data and file extension and save it to an ImageField using the ContentFile class
import uuid
from django.core.files.base import ContentFile
def convert_b64data(b64data, filename):
file_format, imgstr = b64data.split(';base64,')
ext = file_format.split('/')[-1]
return {
'obj': base64.b64decode(imgstr),
'extension': ext,
}
b64data = request.data['b64file']
filename = str(uuid.uuid4())
file_data = convert_b64data(b64data, filename)
file_path = 'media/{}/{}.{}'.format(
user.code,
filename,
file_data['extension']
)
user.banner.save(file_path, ContentFile(file_data['obj']))
In newer versions of Django (I checked on 3.2), you just may need to wrap the file in a ContentFile.
from django.core.files.base import ContentFile
Model.FileField.save('foobar', ContentFile(file))
https://docs.djangoproject.com/en/3.2/ref/files/file/

Categories