Doing this with Google HTTP libraries is tricky because they are designed for synchronous use. You need to put the actual download in a different thread to avoid blocking IOLoop. You can use os.pipe
for communication between the Tornado stream and the download stream (wrap the end of the recording in the tube in PipeIOStream and the end of reading in os.fdopen
). Here's an untested solution sketch:
def prepare(self):
r, w = os.pipe()
self.write_pipe = tornado.iostream.PipeIOStream(w)
self.read_pipe = os.fdopen(r)
self.upload_done = tornado.locks.Event()
self.io_loop = tornado.ioloop.IOLoop.current()
self.thread = threading.Thread(target=self.upload_file)
self.thread.start()
def upload_file(self):
google_client.upload_from_file(self.read_pipe)
self.io_loop.add_callback(self.upload_done.set)
async def data_received(self, chunk):
await self.write_pipe.write(chunk)
async def put(self):
self.write_pipe.close()
await self.upload_done.wait()
self.thread.join()
self.render("upload_done.html")
Alternatively, you can avoid the synchronous Google libraries and do everything using the basic HTTP APIs and AsyncHTTPClient. Sorting authentication this way is complicated, but you avoid thread mismatch. This involves using body_producer, as in that sense
source
share