Google App Engine: cómo escribir archivos grandes en Google Cloud Storage

Estoy tratando de guardar archivos grandes de Blobstore de Google App Engine en Google Cloud Storage para facilitar la copia de seguridad.

Funciona bien para archivos pequeños (<10 mb), pero para archivos más grandes se vuelve inestable y los lanzamientos de GAE y FileNotOpenedError.

Mi código:

PATH = '/gs/backupbucket/' for df in DocumentFile.all(): fn = df.blob.filename br = blobstore.BlobReader(df.blob) write_path = files.gs.create(self.PATH+fn.encode('utf-8'), mime_type='application/zip',acl='project-private') with files.open(write_path, 'a') as fp: while True: buf = br.read(100000) if buf=="": break fp.write(buf) files.finalize(write_path) 

(Se ejecuta en un taskeque para evitar exceder el tiempo de ejecución).

Lanza un FileNotOpenedError:

 Rastreo (llamadas recientes más última):
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1511, en __call__
     rv = self.handle_exception (solicitud, respuesta, e)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1505, en __call__
     rv = self.router.dispatch (solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1253, en default_dispatcher
     return route.handler_adapter (solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1077, en __call__
     return handler.dispatch ()
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 547, en despacho
     devolver self.handle_exception (e, self.app.debug)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 545, en despacho
     método de retorno (* args, ** kwargs)
   Archivo "/base/data/home/apps/s~simplerepository/1.354754771592783168/processFiles.py", línea 249, en la publicación
     fp.write (buf)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 281, en __exit__
     cierre automático ()
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 275, en cierre
     self._make_rpc_call_with_retry ('Cerrar', solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 388, en _make_rpc_call_with_retry
     _make_call (método, solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 236, en _make_call
     _raise_app_error (e)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 179, en _raise_app_error
     elevar FileNotOpenedError ()

He investigado más a fondo y, de acuerdo con un comentario de GAE, número 5371, la API de archivos cierra el archivo cada 30 segundos. No he visto esto documentado en ningún otro lugar.

He intentado solucionar esto cerrando y abriendo el archivo a intervalos, pero ahora obtengo un WrongOpenModeError. El siguiente código se edita desde la primera versión de esta publicación. He agregado una pausa de 0,5 segundos entre el cierre y la apertura del archivo. Ahora lanza un WrongOpenModeError.

Mi código (actualizado):

 PATH = '/gs/backupbucket/' for df in DocumentFile.all(): fn = df.blob.filename br = blobstore.BlobReader(df.blob) write_path = files.gs.create(self.PATH+fn.encode('utf-8'), mime_type='application/zip',acl='project-private') fp = files.open(write_path, 'a') c = 0 while True: if (c == 5): c = 0 fp.close() files.finalize(write_path) time.sleep(0.5) fp = files.open(write_path, 'a') c = c + 1 buf = br.read(100000) if buf=="": break fp.write(buf) files.finalize(write_path) 

Stacktrace:

 Rastreo (llamadas recientes más última):
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1511, en __call__
     rv = self.handle_exception (solicitud, respuesta, e)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1505, en __call__
     rv = self.router.dispatch (solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1253, en default_dispatcher
     return route.handler_adapter (solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 1077, en __call__
     return handler.dispatch ()
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 547, en despacho
     devolver self.handle_exception (e, self.app.debug)
   Archivo "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", línea 545, en despacho
     método de retorno (* args, ** kwargs)
   Archivo "/base/data/home/apps/s~simplerepository/1.354894420907462278/processFiles.py", línea 267, en get
     fp.write (buf)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 310, en escritura
     self._make_rpc_call_with_retry ('Append', solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 388, en _make_rpc_call_with_retry
     _make_call (método, solicitud, respuesta)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 236, en _make_call
     _raise_app_error (e)
   Archivo "/base/python27_runtime/python27_lib/versions/1/google/appengine/api/files/file.py", línea 188, en _raise_app_error
     elevar WrongOpenModeError ()

He intentado encontrar información sobre WrongOpenModeError pero el único lugar donde se menciona es en appengine.api.files.file.py.

Se agradecerán las sugerencias sobre cómo solucionar esto y poder guardar también archivos grandes en el almacenamiento de Google Cloud. ¡Gracias!

Estaba teniendo el mismo problema, al final, escribir un iterador para obtener datos y capturar la excepción, funciona pero es una solución alternativa.

Re-escribir su código sería algo como:

 from google.appengine.ext import blobstore from google.appengine.api import files def iter_blobstore(blob, fetch_size=524288): start_index = 0 end_index = fetch_size while True: read = blobstore.fetch_data(blob, start_index, end_index) if read == "": break start_index += fetch_size end_index += fetch_size yield read PATH = '/gs/backupbucket/' for df in DocumentFile.all(): fn = df.blob.filename br = blobstore.BlobReader(df.blob) write_path = files.gs.create(self.PATH+fn.encode('utf-8'), mime_type='application/zip',acl='project-private') with files.open(write_path, 'a') as fp: for buf in iter_blobstore(df.blob): try: fp.write(buf) except files.FileNotOpenedError: pass files.finalize(write_path) 

En mi files.finalize(write_path) debe files.finalize(write_path) n files.finalize(write_path) en el intervalo, finalize hace que el archivo sea legible y no puede cambiarlo de nuevo.

¿Es backends una opción que puedes elegir? Eso se ejecutará en segundo plano y tiene mucho mayor poder que TaskQueue.