5 votes

Google App Engine : Comment écrire des fichiers volumineux dans Google Cloud Storage ?

J'essaie d'enregistrer des fichiers volumineux depuis le Blobstore de Google App Engine vers Google Cloud Storage pour faciliter la sauvegarde.

Cela fonctionne bien pour les petits fichiers (<10 mb) mais pour les fichiers plus importants, cela devient instable et GAE lance une erreur FileNotOpenedError.

Mon code :

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)

(Exécute dans un taskeque pour éviter de dépasser le temps d'exécution).

Lance une erreur FileNotOpenedError :

Traceback (most recent call last):
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1511, in \_\_call\_\_
    rv = self.handle\_exception(request, response, e)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1505, in \_\_call\_\_
    rv = self.router.dispatch(request, response)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1253, in default\_dispatcher
    return route.handler\_adapter(request, response)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1077, in \_\_call\_\_
    return handler.dispatch()
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 547, in dispatch
    return self.handle\_exception(e, self.app.debug)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 545, in dispatch
    return method(\*args, \*\*kwargs)
  File "/base/data/home/apps/s~simplerepository/1.354754771592783168/processFiles.py", line 249, in post
    fp.write(buf)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 281, in \_\_exit\_\_
    self.close()
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 275, in close
    self.\_make\_rpc\_call\_with\_retry('Close', request, response)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 388, in \_make\_rpc\_call\_with\_retry
    \_make\_call(method, request, response)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 236, in \_make\_call
    \_raise\_app\_error(e)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 179, in \_raise\_app\_error
    raise FileNotOpenedError()

J'ai mené une enquête plus approfondie et, d'après un commentaire à l'adresse suivante Numéro 5371 de la GAE l'API Fichiers ferme le fichier toutes les 30 secondes. Je n'ai vu cela documenté nulle part ailleurs.

J'ai essayé de contourner le problème en fermant et en ouvrant le fichier à intervalles réguliers, mais j'obtiens maintenant une erreur de type WrongOpenModeError. J'ai ajouté une pause de 0,5 seconde entre la fermeture et l'ouverture du fichier. J'obtiens maintenant une erreur de type WrongOpenModeError.

Mon code (mis à jour) :

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 :

Traceback (most recent call last):
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1511, in \_\_call\_\_
    rv = self.handle\_exception(request, response, e)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1505, in \_\_call\_\_
    rv = self.router.dispatch(request, response)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1253, in default\_dispatcher
    return route.handler\_adapter(request, response)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 1077, in \_\_call\_\_
    return handler.dispatch()
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 547, in dispatch
    return self.handle\_exception(e, self.app.debug)
  File "/base/python27\_runtime/python27\_lib/versions/third\_party/webapp2-2.3/webapp2.py", line 545, in dispatch
    return method(\*args, \*\*kwargs)
  File "/base/data/home/apps/s~simplerepository/1.354894420907462278/processFiles.py", line 267, in get
    fp.write(buf)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 310, in write
    self.\_make\_rpc\_call\_with\_retry('Append', request, response)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 388, in \_make\_rpc\_call\_with\_retry
    \_make\_call(method, request, response)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 236, in \_make\_call
    \_raise\_app\_error(e)
  File "/base/python27\_runtime/python27\_lib/versions/1/google/appengine/api/files/file.py", line 188, in \_raise\_app\_error
    raise WrongOpenModeError()

J'ai essayé de trouver des informations sur l'erreur WrongOpenModeError mais le seul endroit où elle est mentionnée est dans le fichier appengine.api.files.file.py lui-même.

Des suggestions sur la manière de contourner ce problème et de pouvoir enregistrer des fichiers volumineux sur le stockage Google Cloud seraient très appréciées. Merci de votre compréhension.

3voto

Janusz Skonieczny Points 1587

IMO vous devriez files.finalize(write_path) sur l'intervalle, finalize rend le fichier lisible et il n'est plus possible de le rendre à nouveau inscriptible.

1voto

sebastian serrano Points 574

J'ai eu le même problème, j'ai fini par écrire un itérateur autour des données et attraper l'exception, ça marche mais c'est une solution de contournement.

La réécriture de votre code serait quelque chose comme :

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)

-1voto

vishal.biyani Points 1550

Les backends sont-ils une option que vous pouvez choisir ? Cela fonctionnera en arrière-plan et aura beaucoup plus de puissance que TaskQueue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X