In my Google Apps Script web app,
I am trying to upload an excel file to google drive using a post request received on the web app.
I don't have much control over the way the excel file is sent.
It is currently being sent in a raw format like this :
PKj7VN?Ȗ[Content_Types].xml???N?0E???NY ??tQ` H?0?$???iI??)B?EW~?;sfFSm'??BT??t?JJ?J?*???c?\??mS?O"IVk: ?Σ??Ȝ???#0=CϽ???ߖ???"X,p?A??:q?H????`?H?n1ά?
ﵒ?Ώ??C)??"?'?Ǜd?|?0+???4??Z?W%?딄?ɃI!,?????ɌP6?F?D?aRV>i??????Ȯ?Ǔ???^??????R?Y?<?+N`?wi?????S??|PKj7V???_rels/.rels??A?0D????]c(l? [???Sh?V??ۥ.'3?&S5???C4?l?:E?qZ??;o???g?r"??G?+.
S?GΣ??X?G?????)ˠ??j???,?<|2`?d???e?O
How do I convert this string to a blob and upload it to google drive. What I have tried is this :
function doPost(e) {
var binaryData = e.postData.contents //string
var file = DriveApp.createFile(Utilities.newBlob(binaryData,'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', "myExcelFile.xlsx"));
return ContentService.createTextOutput("File uploaded successfully!");
}
The file did get uploaded but it's corrupt and not viewable.
Edit :
I am able to accomplish this on local machine using python but not able to replicate it on google apps script.
from flask import Flask, request
import io
import pandas as pd
app = Flask(__name__)
#app.route('/save', methods=['POST'])
def save_to_excel():
# Get the raw request data
data = request.data
print(type(data)) #<class 'bytes'>
toread = io.BytesIO()
toread.write(data)
toread.seek(0)
df = pd.read_excel(toread)
file_name = 'sampleData.xlsx'
df.to_excel(file_name)
# Create a new Excel workbook
return "Data saved as xlsx file: {}"
Related
I have an endpoint that takes in a csv file given by the user, the function then performs some analysis on it, and the user should then have the report downloaded onto their system.
the code looks similar to this currently:
function uploadFile(file) {
var form = new FormData();
form.append('file', file);
var request = new XMLHttpRequest();
request.open('POST', '/upload');
request.send(form);
}
#app.route("/upload", methods=["POST"])
def endpoint_function():
file = flask.request.files["file"]
analysis_function(pd.read_csv(file)) # Outputs csv to 'filepath'
return flask.send_file(filepath, as_attachment=True)
When the function is triggered from the frontend, it creates the csv, but does not download it to the users system.
I have checked that the report csv is being correctly placed at the filepath.
I wrote a minimum working example of downloading a file from a flask route:
The directory of this app contains two files:
app.py - python script
download.txt - file to be downloaded
app.py:
# app.py
from flask import Flask, send_file
app = Flask(__name__)
#app.route('/')
def index():
return " Download File"
#app.route('/download')
def download_file():
return send_file("download.txt", as_attachment=True)
if __name__ == '__main__':
app.run(debug=True, port=8000, host='127.0.0.1')
Test this code on your machine, if its failing, maybe you have a setting on your browser/firewall to block file downloads.
With the JavaScript, you'll need to await the response from the server, then you should be able to download it as would another file.
Here's a link to a post that shows you how to await the XMLHttpRequest: In JavaScript how do I/should I use async/await with XMLHttpRequest?
Hope this helps!
I am building a developer tool which allows the user to query device data for a given time window from a DB, generates plots of the data using matplotlib, saves them to local files, and renders them on a webpage. I've used create-react-app for the frontend and a Flask backend, both of which are run locally. The issue is that when the backend creates a new directory for the plots, the react app reloads before the plots are even generated, and I get a 'Request aborted' error on the browser. The strangest thing is that it does not break on the first request, and embeds all the plots correctly. It breaks only on subsequent requests. How do I get around this?
Here's the plotter code that creates the directory and saves files:
fig_latest = plt.figure()
ax_latest = fig_latest.add_subplot(projection='3d')
plot_latest = ax_latest.bar3d(x, y, bottom, width, depth, latest_height, color='c')
fig_ani= plt.figure()
ax_ani = fig_ani.add_subplot(projection='3d')
def update_plot(frame, data):
for bar in ax_ani.collections:
ax_ani.collections.pop()
frame_data = data[frame].to_numpy().astype(np.int16)
height = frame_data.ravel()
print(f'Frame: {int(frame)}')
timestamp = time.strftime('%d %b %Y, %I:%M:%S %p', time.localtime(int(frame/1000)))
for text in ax_ani.texts:
text.set_visible(False)
ax_ani.text2D(0.6, 1, f'Time: {timestamp}', transform=ax_ani.transAxes)
return [ax_ani.bar3d(x, y, bottom, width, depth, height, color='c')]
bar_ani = animation.FuncAnimation(fig_ani, update_plot, frames=row_data.keys(), fargs=(row_data,), interval=750)
os.mkdir(path_name)
mpeg_writer = animation.FFMpegWriter(fps=2)
bar_ani.save(os.path.join(path_name, 'animation.mp4'), writer=mpeg_writer)
fig_latest.savefig(os.path.join(path_name, 'latest.png'))
The app reloads right after os.makedir is called.
Flask backend:
from flask import Flask, request
from flask_cors import CORS, cross_origin
import plotter
import json
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "http://localhost:3001"}})
#app.route('/plots', methods=['POST', 'OPTIONS'])
#cross_origin(origin='localhost')
def respond():
data = request.json
device_id = data['deviceId']
start_time = data['startTime']
end_time = data['endTime']
print(f'{device_id}, {start_time}, {end_time}')
return plotter.build_plots(device_id, start_time, end_time)
Code for the POST request on the frontend:
const baseUrl = 'http://localhost:3001/plots'
const requestPlot = (deviceId, startTime, endTime) => {
const plotRequest = {
deviceId,
startTime,
endTime
}
console.log(plotRequest)
return axios
.post(baseUrl, plotRequest)
.then(response => {
console.log('RECEIVED RESPONSE!')
return response.data
})
}
Seems like you are running your frontend application in "development" mode via "npm start" command. Since webpack is watching all file changes and reloads frontend on each file editied or created. One way to fix it - is to run "npm build" command and setup nginx or run your frontend with SimpleHTTP server of python in "dist" directory
I created a web scraper using Puppeteer to extract data from https://www.jobnimbus.com/ and would like to download a CSV file generated by JobNimbus. When I send a GET request to the CSV download URL, I receive .aspx file type instead of .csv.
JobNimbus requires a login so I'm only showing a sample of the code I'm using:
require("dotenv").config();
const download = require("download");
(async () => {
let url =
`https://app.jobnimbus.com/ReportDownload.aspx?type=csv&id=${process.env.USER_ID}&rid=null`;
await download(url, "output");
})();
It downloads ReportDownload.aspx which appears to be HTML. How do I download this file as CSV? Thank you in advance for your help!
https://i.imgur.com/lX7eIdA.png
I am working on a project where I am using Django as the back end. I am working with Django rest framework, and I have an API to download a File.
#detail_route(methods=['GET'], permission_classes=[IsAuthenticated])
def test(self, request, pk=None):
try:
ticket = Ticket.objects.get(id=pk, user=request.user)
file_path = ticket.qrcode_file.path
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="image/jpeg")
name = "%s %s %s %s.jpg" % (ticket.show.title, datetime.strftime(ticket.show.date_time,
"%H_%M_%p"),
datetime.strftime(ticket.show.date_time, "%d %B %Y"), ticket.id)
response['Content-Disposition'] = "attachment; filename=%s" % name.replace(" ", "_")
return response
return Response({'error': 'Ticket doest not belong to requested user.'}, status=status.HTTP_403_FORBIDDEN)
except Ticket.DoesNotExist as e:
return Response({'error': str(e)}, status=status.HTTP_404_NOT_FOUND)
On the front-end I am using Nuxtjs (ssr for vuejs). This is a little snippet of code, where a user can download the file by clicking a link of target blank.:
<a class="downloadBtn" target="_blank" :href="`${baseURL}/payments/api/tickets/${ticket.id}/download_ticket/`">Download e-ticket</a>
The web app is running on Nuxtjs server (localhost:3000) and Django server is running on localhost:8000, only the API is used to communicate between the Nuxtjs and Django by using the auth token.
When I click the download link it opens up a new tab and make a request from that new tab, where no token is passed with the request. And since, the django view to download the ticket is permission_classes=[IsAuthenticated] I cannot be authenticated as request.user is anonymous.
Is there any other way that I can make it work to download the file by checking whether the requested user is the Ticket's owner?
Because you are using JWT, you should download the file from your frontend after requesting it from your api using some sort of ajax request and the JWT header.
some_api_request_hendler().then(function (response) {
var file = new Blob([response.data], {type: response.headers("Content-Type")});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(pdf);
link.download = "the_new_file_name";
link.click();
});
I'm using this answer as an example. Your API should not be changed. The way you did it is the way to go.
And your link now need to call your new frontend function instead of just <a href.
Edit:
I asked a question like that a few years ago. You might find some help there.
Browser Compatibility
blob, createElement, createObjectURL
I am experimenting with Python's Eventlet Websocket support, using this simple example :
import eventlet
from eventlet import wsgi
from eventlet import websocket
from eventlet.support import six
# demo app
import os
import json
import time
import random
#websocket.WebSocketWSGI
def handle(ws):
""" This is the websocket handler function. Note that we
can dispatch based on path in here, too."""
if ws.path == '/echo':
while True:
ws.send("hello")
#ws.send(json.dumps({"msg":"hello"}))
time.sleep(1)
elif ws.path == '/data':
for i in six.moves.range(10000):
ws.send("0 %s %s\n" % (i, random.random()))
eventlet.sleep(0.1)
def dispatch(environ, start_response):
""" This resolves to the web page or the websocket depending on
the path."""
if environ['PATH_INFO'] == '/data':
return handle(environ, start_response)
else:
start_response('200 OK', [('content-type', 'text/html')])
return [open(os.path.join(
os.path.dirname(__file__),
'websocket.html')).read()]
if __name__ == "__main__":
# run an example app from the command line
listener = eventlet.listen(('127.0.0.1', 7000))
print("\nVisit http://localhost:7000/ in your websocket-capable browser.\n")
wsgi.server(listener, dispatch)
I'm not going to include the entire websocket handler I have in the Javascript, just the ws.onmessage method:
ws.onmessage = function (evt)
{
console.log(evt.data)
var received_msg = evt.data;
#Do stuff, i.e var obj = JSON.parse(received_msg)
#callback(obj)
};
The console.log(evt.data) indicates a succesful connection with the websocket (and you can assume this is all fine). However, the logging shows Blob {size: 31, type: ""} as the content of evt.data. I assume this is some kind of response object which is interpreted as binary (file) data (though I might be entirely wrong), but I'm not sure what to do with this.
I see that Blob data is often the data type for file-like objects. I suppose I could approach it as such, but I really only want to send json data back and forth. I've tried dumping a dict as a JSON and sending that, but it did the same. Even a string is received in Blob format.
How do I use eventlet for json data transmission?
As of 2017-05 Eventlet websocket API does not support string websocket frames. Patches are welcome, it's easy.
Your options:
read Blob at javascript end FileReader.readAsText
use another websocket library (pure Python implementation will work fine with Eventlet patching)
add string frame support to Eventlet websocket library