Flask downloads previous file version in response - python

I'm running a flask application that downloads a zip file that the user created.
The zipfile is deleted/overwritten with the same name for the user in the directory.
Then, when I get the download link, it downloads the old zipfile with the deleted files that cant be opened.
The link works correctly on incognito mode.
However in regular chrome, the request doesn't hit the test server before serving the zip file.
Anyone know what the issue might be?
#page.route('/response/<id>')
def response(id):
user = User.query.filter(User.spreadsheet_id.any(id)).first()
print(user.spreadsheet_id)
zip_name = f'{user.email}_zip.zip'
path = ''
root_dir = os.path.dirname(os.getcwd())
print(os.path.join(root_dir, 'app', zip_name))
return send_file(os.path.join(root_dir, 'app', zip_name), mimetype='zip', attachment_filename=zip_name, as_attachment=True)

in my case the solution was to wrap the attachment in a Response object and add a cache-control header:
attachment = send_file(os.path.join(root_dir, 'app', zip_name), mimetype='zip', attachment_filename=zip_name, as_attachment=True)
resp = make_response(attachment)
resp.cache_control.max_age = 120
return resp

Related

File not found when deployed on server, but works fine when app is tested locally

I've been creating a simple invoicing web app using Python and Flask. One of its functionalities is that it automatically generates the PDF of the invoice once and sends it to the designated email address. Here is the route and function for that specific part: (Which is the only route and function I'm running into errors with)
#forms.route("/sendasemail/<int:id>", methods=['GET'])
#login_required
def sendasemail(id):
order = Order.query.get(id)
products = order.products
rendered = render_template("invoice.html", order = order, products = products)
save_location = url_for('static', filename=f'Order_{id}.pdf')
pdf = pdfkit.from_string(rendered, save_location)
msg = EmailMessage()
msg['Subject'] = f"Order Form No. {id}"
msg['From'] = 'DESIGNATED EMAIL'
if current_user.username == 'USERNAME':
msg['To'] = 'DESIGNATED_EMAIL'
else:
msg['To'] = 'DESIGNATED_EMAIL'
msg.set_content(f"Hi, \n\nKindly find Order Form No.{id} attached to this email.\n\nRegards,\nShibam S.P. Trading")
with open(f'Order_{id}.pdf', 'rb') as f:
file_data = f.read()
file_name = f.name
msg.add_attachment(file_data, maintype='application', subtype='octet-stream', filename=file_name)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login('DESIGNATED_EMAIL', 'DESIGNATED_PASSWORD')
smtp.send_message(msg)
os.unlink(f'Order_{id}.pdf')
flash("Email sent!", "success")
return render_template('show_data.html', order = order, products = products)
This snippet of code depends on the wkhtmltopdf and PDFKit libraries to function. When I run this code locally, it works perfectly fine. However, when I try running the same function within the application deployed on a production server, it throws an Internal Server Error. I think it might be because the PDF file, once created, cannot be found by the program due to some directory restructuring that I'm missing out on or plainly don't understand.
In the save_location = url_for('static', filename=f'Order_{id}.pdf') you create an absolute URL path, e.g. /static/Order_1.pdf instead of a local relative file path, like Order_1.pdf. But later in the script you use the local file path.
So the quick fix is just to use the local file path for save_location as well:
save_location = f'Order_{id}.pdf'
This assumes that the root directory of the Flask app is writable by the process. If not, you have to use a temporary directory, e.g. /tmp/ or an equivalent on your production server and update each file path in the script accordingly.

Django don't send email message with attached zip file

everyone! I have a problem with sending an email with Django smtp email backend and SendGrid. My view is below.
It makes batch of pdf files with pyppeteer, zip them to archive, attaches to the message and then send it to the email. It should be, but it doesn't work!
I checked it with other view for email sending(a little bit diff logic without zip or something.) and it worked but this means that it's not the problem with sendgrid and any other stuff like secret keys in uwsgi configuration.
The fact is, emails don't stuck in send grid list.
I tried to put some dummy bites instead of pdf file like pdf_file = b'some bytes' and it worked on my local machine but didn't work on server. I got the size of zip-archive from a response. Please explain to me where I'm wrong.
### views.py
#csrf_exempt
#require_POST
def generate_batch_pdf(request, blueprint):
try:
payload = json.loads(request.body)
email = payload.get('email')
uuids = payload.get('uuids')
zip_buffer = io.BytesIO()
compression = zipfile.ZIP_DEFLATED
with zipfile.ZipFile(zip_buffer, 'a', compression, False) as f:
for uuid in uuids:
report_name = _report_name(uuid, blueprint)
pdf_file = async_to_sync(generate_pdf_file)(
request.get_host(),
uuid,
blueprint,
report_name,
)
f.writestr(
zinfo_or_arcname='{0}'.format(report_name),
data=pdf_file,
)
msg = EmailMessage(
subject='Отчеты Тест',
body='Архив выбранных отчетов',
from_email=settings.DEFAULT_FROM_EMAIL,
to=[email],
)
msg.attach('reports.zip', zip_buffer.getvalue(), 'application/zip')
msg.send(fail_silently=False)
return JsonResponse(
{
'msg': 'Ok',
# it's here just for check
'size': '{0}'.format(sys.getsizeof(zip_buffer)),
},
)
except Exception as e:
return JsonResponse({'msg': 'Err -> {e}'.format(e=e)})
I'm using python 3.6
### requirements.txt
django==3.1
...

Problem using Dropbox API in python with OAuth2 code

I am trying to set up an app using Dropbox as the medium for users to share files.
I am able to upload a file using the App token, but when I try using the authorization code the file does not upload; the application does not report an error.
The code that follows contains both methods:
dbx = get_dbx_with_token()
works, but
dbx = get_dbx_with_auth_code()
does not. Any help appreciated.
import webbrowser
from dropbox import Dropbox
from dropbox.files import WriteMode
from dropbox import DropboxOAuth2FlowNoRedirect
APP_KEY = '<my app key>'
APP_SECRET = '<my app secret>'
APP_TOKEN = '<my app token>'
def upload(dbx):
local_file = '<path to local file>'
remote_file = '<remote file name>'
with open(local_file, 'rb') as f_upload:
try:
foo = dbx.files_upload(f_upload.read(), remote_file,
mode=WriteMode('overwrite'))
print('done ...', foo)
except:
print('Upload error')
def get_dbx_with_auth_code():
auth_flow = DropboxOAuth2FlowNoRedirect(APP_KEY, APP_SECRET)
authorize_url = auth_flow.start()
webbrowser.open(authorize_url)
auth_code = input('Authorization code: ').strip()
try:
oauth_result = auth_flow.finish(auth_code)
except:
print('Token error')
return None
dbx = Dropbox(oauth_result.access_token)
return dbx
def get_dbx_with_token():
dbx = Dropbox(APP_TOKEN)
return dbx
if __name__ == '__main__':
dbx = get_dbx_with_token()
#dbx = get_dbx_with_auth_code()
upload(dbx)
The dbx.files_upload function (foo) returns:
FileMetadata(
name='uploaded.txt',
id='id:<my_id>',
client_modified=datetime.datetime(2018, 12, 13, 18, 24, 15),
server_modified=datetime.datetime(2018, 12, 13, 18, 24, 15),
rev='013000000010ede3870', size=6, path_lower='/upload test/uploaded.txt',
path_display='/upload test/uploaded.txt',
parent_shared_folder_id=None,
media_info=None,
symlink_info=None,
sharing_info=None,
property_groups=None,
has_explicit_shared_members=None, content_hash='<content hash>')
the FileMetadata for get_dbx_with_token is different:
parent_shared_folder_id='1234567890',
sharing_info=FileSharingInfo(read_only=False,
parent_shared_folder_id='1234567890',
modified_by='dbid:AAAyXwp1wvSzPzmqzCJ9SWFuxhc')
(BTW the folder I am uploading to is shared folder belonging to another user)
The reason that I think it does not work is that it does not appear in my (browser) Dropbox folder if I use get_dbx_with_auth_code(), but it does if I use get_dbx_with_token().
Thanks to Greg's comments I realise that the upload was in fact taking place, but my limited understanding of the API meant that I didn't realise that using the auth_code option placed the file in a specific folder for the app. That's fine now that I know where to look for it.
The output indicates that the file was successfully uploaded, so it sounds like you're looking in a different folder or account when looking for it on the Dropbox web site. Double check what account/folder you're looking in. Also, you may be using a different app between the two flows, so note that if an app has the "app folder" permission, it will upload into the special "app folder" made for the app, by default inside "/Apps"
Printing the output of files_upload showed what was going on.

How to download several files with GAE Python

I'd like to download several files with GAE Python code.
My current code is like below
import webapp2, urllib
url1 = 'http://dummy/sample1.jpg'
url2 = 'http://dummy/sample2.jpg'
class DownloadHandler(webapp2.RequestHandler):
def get(self):
#image1
self.response.headers['Content-Type'] = 'application/octet-stream'
self.response.headers['Content-Disposition'] = 'attachment; filename="' + 'sample1.jpg' + '"'
f = urllib.urlopen(url1)
data = f.read()
self.response.out.write(data)
#image2
self.response.headers['Content-Type'] = 'application/octet-stream'
self.response.headers['Content-Disposition'] = 'attachment; filename="' + 'sample2.jpg' + '"'
f = urllib.urlopen(url2)
data = f.read()
self.response.out.write(data)
app = webapp2.WSGIApplication([('/.*', DownloadHandler)],
debug=True)
I expected to occur download dialogue twice with this code, but actually occurred once, and only sample2.jpg was downloaded.
How can you handle download dialogue several times?
I'd actually like to realize some other functions adding above as well.
To display progressing message on the browser such as
sample1.jpg was downloaded
sample2.jpg was downloaded
sample3.jpg was downloaded ...
And redirect to the other page after downloading files.
When I wrote a code such as
self.redirect('/otherpage')
after
self.response.out.write(data)
Only redirect had happened and didn't occur download procedure.
Would you give me any ideas to solve it please.
I'm using python2.7
Two things.
You cannot write two files in one response that has a Content-Type of application/octet-stream. To stuff multiple files in in the response, you would have to encode your response with multipart/form-data or multipart/mixed and hope that the client would understand that and parse it and show two download dialogues
Once you've already called self.response.out.write(…), you shouldn't be setting any more headers.
To me it seems that the most foolproof option would be to serve an HTML file that contains something like:
<script>
window.open('/path/to/file/1.jpg');
window.open('/path/to/file/1.jpg');
</script>
… and then handle those paths using different handlers.
Another option would be to zip the two files and serve the zipfile to the client, though it may or may not be preferable in your case.
I reached the goal what I wanted to do.
As user interaction, generating html sources include below
<script type="text/javascript">
window.open("/download?url=http://dummy/sample1.jpg")
window.open("/download?url=http://dummy/sample2.jpg")
</script>
then created new windows are handled with this code.
class DownloadHandler(webapp2.RequestHandler):
def get(self):
url = self.request.get('url')
filename = str(os.path.basename(url))
self.response.headers['Content-Type'] ='application/octet-stream'
self.response.headers['Content-Disposition'] = 'attachment; filename="%s"' % (filename)
data = urllib.urlopen(url).read()
self.response.out.write(data)
app = webapp2.WSGIApplication([('/download', DownloadHandler)], debug=True)
Thank you, Attila.

Dropbox Python API: dropbox.rest.ErrorResponse: [404] u"Path '<path>' not found"

Having successfully run the 'Getting started with Core API for Python', I'm now attempting to print a file's shared link by doing:
def get_file_information(self, file_path):
file_info = client.DropboxClient(self.sess)
print file_info.share(file_path)
I've tried passing:
file_path = '/Users/augustoesteves/Dropbox/DSCN7334.mov'
file_path = '/Dropbox/DSCN7334.mov'
file_path = '/DSCN7334.mov'
But I always get:
dropbox.rest.ErrorResponse: [404] u"Path '/Users/augustoesteves/Dropbox/DSCN7334.mov' not found"
dropbox.rest.ErrorResponse: [404] u"Path 'Dropbox/DSCN7334.mov' not found"
dropbox.rest.ErrorResponse: [404] u"Path 'DSCN7334.mov' not found"
I must be doing something embarrassingly stupid, but I can't figure it out.
Dropbox expects the request url to be in the form: https://api.dropbox.com/1/shares/<root>/<path> where root is either dropbox or sandbox and path if the file path. The share() method of Python API constructs the request url in the form:
path = "/shares/%s%s" % (self.session.root, format_path(path))
self.session.root is set depending on access_type value passed to the session constructor:
self.root = 'sandbox' if access_type == 'app_folder' else 'dropbox'
So your 3rd url should be correct. Check your access_type and path. Try to construct full URL and send a request manually.

Categories

Resources