send data from blobstore as email attachment in GAE - python

Why isn't the code below working? The email is received, and the file comes through with the correct filename (it's a .png file). But when I try to open the file, it doesn't open correctly (Windows Gallery reports that it can't open this photo or video and that the file may be unsupported, damaged or corrupted).
When I download the file using a subclass of blobstore_handlers.BlobstoreDownloadHandler (basically the exact handler from the GAE docs), and the same blob key, everything works fine and Windows reads the image.
One more bit of info - the binary files from the download and the email appear very similar, but have a slightly different length.
Anyone got any ideas on how I can get email attachments sending from GAE blobstore? There are similar questions on S/O, suggesting other people have had this issue, but there don't appear to be any conclusions.
from google.appengine.api import mail
from google.appengine.ext import blobstore
def send_forum_post_notification():
blob_reader = blobstore.BlobReader('my_blobstore_key')
blob_info = blobstore.BlobInfo.get('my_blobstore_key')
value = blob_reader.read()
mail.send_mail(
sender='my.email#address.com',
to='my.email#address.com',
subject='this is the subject',
body='hi',
reply_to='my.email#address.com',
attachments=[(blob_info.filename, value)]
)
send_forum_post_notification()

I do not understand why you use a tuple for the attachment. I use :
message = mail.EmailMessage(sender = ......
message.attachments = [blob_info.filename,blob_reader.read()]

I found that this code doesn't work on dev_appserver but does work when pushed to production.

I ran into a similar problem using the blobstore on a Python Google App Engine application. My application handles PDF files instead of images, but I was also seeing a "the file may be unsupported, damaged or corrupted" error using code similar to your code shown above.
Try approaching the problem this way: Call open() on the BlobInfo object before reading the binary stream. Replace this line:
value = blob_reader.read()
... with these two lines:
bstream = blob_info.open()
value = bstream.read()
Then you can remove this line, too:
blob_reader = blobstore.BlobReader('my_blobstore_key')
... since bstream above will be of type BlobReader.
Relevant documentation from Google is located here:
https://cloud.google.com/appengine/docs/python/blobstore/blobinfoclass#BlobInfo_filename

Related

Fatal error reading PNG image file: Not a PNG file in Ubuntu 20.04 LTS

I try to download an image using requests module in python.It works but when i try to open this image it showing "Fatal error reading PNG image file: Not a PNG file". Here is my error screenshot.And the code i used to download is,
import requests
img_url = "http://dimik.pub/wp-content/uploads/2020/02/javaWeb.jpg"
r = requests.get(img_url)
with open("java_book.png","wb") as f:
f.write(r.content)
And i run my code in terminal just saying, python3 s.py (s.py is the name of file).
Is something wrong in my code or something else in my operating system(ubuntu 20.04 LTS)?
import requests
response = requests.get("https://devnote.in/wp-content/uploads/2020/04/devnote.png")
file = open("sample_image.png", "wb")
file.write(response.content)
print (response.content)
file.close()
https://devnote.in/wp-content/uploads/2020/04/devnote.png this url is Disable mod_security. so this return error like :
<html><head><title>Not Acceptable!</title></head><body><h1>Not Acceptable!</h1><p>An appropriate representation of the requested resource could not be found on this server. This error was generated by Mod_Security.</p></body></html>.
Disable mod_security using .htaccess on apache server
Mod_security can be easily disabled with the help of .htaccess.
<IfModule mod_security.c>
SecFilterEngine Off
SecFilterScanPOST Off
</IfModule>
It's because you tried to save javaWeb.jpg (A JPG file) as java_book.png (A PNG file).
In an attempt to see what we are working on, I've tried replicating the issue, please see below what found out.
1.) The file you are attempting to open is the ENTIRE HTML document. I can support this, because we are finding !DOCTYPE html at the beginning of your 'wb' or WRITE BINARY command.
<---------------------------------------------- WE ARE AT AN IMPASSE
From here we have a few options to solve our problem.
a.) We could simply download the image from the web page - placing it in a local folder/directory/ or wherever you want it. This is by far our easiest call, because it allows us to call and open it for later without having to do too much. While I'm on a Windows machine - Ubuntu should have no problem doing this either (Unless you aren't in an UBUNTU with a GUI - that can be fixed with startx IF SUPPORTED)
b.) If you have to pull directly from the site itself, you could try something like this using BEAUTIFULSOUP from this answer here. Honestly, I've never really used the latter option since downloading and moving is much more effective.
You just need to save the image as a JPG.
import requests
img_url = "http://dimik.pub/wp-content/uploads/2020/02/javaWeb.jpg"
r = requests.get(img_url)
with open("java_book.jpg","wb") as f:
f.write(r.content)
Yeah, it's a full HTML document:

Posting files to a chat through Slack API

I'm trying to deliver videos, through Slack API using Python's library slackclient.
I often use slack.api_call('chat.postMessage'...) and I am familiar with 'files.upload' but when I execute
slack = SlackClient(TOKEN)
slack.api_call('files.upload', file=open('video.mp4', 'rb')...)
the file is uploaded to the given channel, but is not posted as a message.
What I am trying to achieve is to create a message which I can send as a private message or to a channel that would look something like this
and maybe add some text above it if possible.
I've explored the Attachment section in the docs, but couldn't find anything related to files.
If there is a way to not supply the file in binary format, but as a link that would also be ok (as long as it is displayed in an embedded fashion).
How about this sample script? It uses io.BytesIO(f.read()) for the file. In order to use this, files:write:user has to be included in the scopes. About the text, you can import it using initial_comment. In my environment, attachments could not be used for files.upload. The API document is https://api.slack.com/methods/files.upload.
Script :
with open('./sample.mp4', 'rb') as f:
slack.api_call(
"files.upload",
channels='#sample',
filename='sample.mp4',
title='sampletitle',
initial_comment='sampletext',
file=io.BytesIO(f.read())
)
Result :
If I misunderstand your question, I'm sorry.
I came across this question because I had the same issue - my file would upload and I would get a response, but the file would not be posted to the channel I had sent. It turned out to be a poor job by me of reading the Slack API documentation. I had used chat.postMessage many times and included a single 'channel' argument. Here is that API: https://api.slack.com/methods/chat.postMessage
The files.upload method it wants a comma separated list of channels in a 'channels' argument. See https://api.slack.com/methods/files.upload Once I changed from 'channel' to 'channels' and made sure to pass it as a list, I was successfully posting the image to the channel I wanted.
To the original question then, in your link to the code you used (https://ibb.co/hwH5hF) try changing channel='bla'to channels=['bla']
This works for me:
import slack
client = slack.WebClient(token='xoxb-XXX')
with open('/path/to/attachment.jpeg', 'rb') as att:
r = client.api_call("files.upload", files={
'file': att,
}, data={
'channels': '#my_channel',
'filename': 'downloaded_filename.jpeg',
'title': 'Attachment\'s title',
'initial_comment': 'Attachment\'s description',
})
assert r.status_code == 200

BlobInfo object from a BlobKey created using blobstore.create_gs_key

I am converting code away from the deprecated files api.
I have the following code that works fine in the SDK server but fails in production. Is what I am doing even correct? If yes what could be wrong, any ideas how to troubleshoot it?
# Code earlier writes the file bs_file_name. This works fine because I can see the file
# in the Cloud Console.
bk = blobstore.create_gs_key( "/gs" + bs_file_name)
assert(bk)
if not isinstance(bk,blobstore.BlobKey):
bk = blobstore.BlobKey(bk)
assert isinstance(bk,blobstore.BlobKey)
# next line fails here in production only
assert(blobstore.get(bk)) # <----------- blobstore.get(bk) returns None
Unfortunately, as per the documentation, you can't get a BlobInfo object for GCS files.
https://developers.google.com/appengine/docs/python/blobstore/#Python_Using_the_Blobstore_API_with_Google_Cloud_Storage
Note: Once you obtain a blobKey for the GCS object, you can pass it around, serialize it, and otherwise use it interchangeably anywhere you can use a blobKey for objects stored in Blobstore. This allows for usage where an app stores some data in blobstore and some in GCS, but treats the data otherwise identically by the rest of the app. (However, BlobInfo objects are currently not available for GCS objects.)
I encountered this exact same issue today and it feels very much like a bug within the blobstore api when using google cloud storage.
Rather than leveraging the blobstore api I made use of the google cloud storage client library. The library can be downloaded here: https://developers.google.com/appengine/docs/python/googlecloudstorageclient/download
To access a file on GCS:
import cloudstorage as gcs
with gcs.open(GCSFileName) as f:
blob_content = f.read()
print blob_content
It sucks that GAE has different behaviours when using blobInfo in local mode or the production environment, it took me a while to find out that, but a easy solution is that:
You can use a blobReader to access the data when you have the blob_key.
def getBlob(blob_key):
logging.info('getting blob('+blob_key+')')
with blobstore.BlobReader(blob_key) as f:
data_list = []
chunk = f.read(1000)
while chunk != "":
data_list.append(chunk)
chunk = f.read(1000)
data = "".join(data_list)
return data`
https://developers.google.com/appengine/docs/python/blobstore/blobreaderclass

Saving to S3, but file is Blank

I was using this answer to upload a PNG to S3:
https://stackoverflow.com/a/6693577/815878
The file is being uploaded to S3 however whenever I double click on the image to display it the url is "about:blank" and the screen is blank.
When I download the image, it is showing up on my computer as the image I saved. My last recourse was to manually test out the url. I made the photo public then tried:
https://s3.amazonaws.com/BUCKET_NAME/IMAGE_NAME.png
which gives me this:
Is there another step from the answer above that is making the file upload improperly? I'm going to paste my code (which is very similar to the link above) just in case...
image = Image.open(self.image)
conn = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
out_im2 = cStringIO.StringIO()
image.save(out_im2, 'PNG')
b = conn.get_bucket('new_test_bucket')
k = b.new_key(self.title+'.png')
k.set_contents_from_filename(out_im2.getvalue())
I'm more of a PHP than a Python guy, but from what I know Amazon S3 Requires defining the type of the file.
You need to send a mime type (e.g. image/png) for the server to recognize the file, since the S3 isn't an actual web server, it doesn't care much for the extension of your file. You could just as much call it "dipididoo.moo" and as long as the type is image/png , it would work.
Set mimetype as below:
k.set_contents_from_filename(out_im2.getvalue(), {'Content-Type' : 'image/png'})

Downloaded filename with Google App Engine Blobstore

I'm using the Google App Engine Blobstore to store a range of file types (PDF, XLS, etc) and am trying to find a mechanism by which the original filename of the uploaded file - as stored in blob_info - can be used to name the downloaded file i.e. so that the user sees 'some_file.pdf' in the save dialogue rather than 'very_long_db_key.pdf'.
I can't see anything in the docs that would allow this:
http://code.google.com/appengine/docs/python/blobstore/overview.html
I've seen hints in other posts that you could use the information in blob_info to set the content-disposition header. Is this the best approach to achieving the desired end?
There is an optional 'save_as' parameter in the send_blob function. By default this is set to False. Setting it to True will cause the file to be treated as an attachment (ie it will trigger a 'Save/Open' download dialog) and the user will see the proper filename.
Example:
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
resource = str(urllib.unquote(resource))
blob_info = blobstore.BlobInfo.get(resource)
self.send_blob(blob_info,save_as=True)
It is also possible to overwrite the filename by passing in a string:
self.send_blob(blob_info,save_as='my_file.txt')
If you want some content (such as pdfs) to open rather than save you could use the content_type to determine the behavior:
blob_info = blobstore.BlobInfo.get(resource)
type = blob_info.content_type
if type == 'application/pdf':
self.response.headers['Content-Type'] = type
self.send_blob(blob_info,save_as=False)
else:
self.send_blob(blob_info,save_as=True)
For future reference, save_as and the BlobstoreDownloadHandler is documented here:
http://code.google.com/appengine/docs/python/tools/webapp/blobstorehandlers.html
It does seem like it should be a bit easier to find. Let's see if it can be improved.
Another option is to append the file name to the end of the download URL. For example:
/files/AMIfv95HJJY3F75v3lz2EeyvWIvGKxEcDagKtyDSgQSPWiMnE0C2iYTUxLZlFHs2XxnV_j1jdWmmKbSVwBj6lYT0-G_w5wENIdPKDULHqa8Q3E_uyeY1gFu02Iiw9xm523Rxk3LJnqHf9n8209t4sPEHhwVOKdDF2A/prezents-list.doc
If you use Jinja2 for templating, you can construct such an URL like this:
{{file.filename}}
then you should adapt your URL mapping accordingly to something like this:
('/files/([^/]+)/?.*', DownloadHandler)
If you have the blob key in the URL, you can ignore the file name in your server-side code.
The benefit of this approach is that content types like images or PDF open directly in the browser, which is convenient for quick viewing. Other content types will just be saved to disk.
Yes it is the best approach; just query the BlobInfo object using the given Blobstore key and use its content-type property.

Categories

Resources