Subir archivos en Google App Engine

79

Estoy planeando crear una aplicación web que permita a los usuarios degradar sus archivos de proyecto de Visual Studio. Sin embargo, parece que Google App Engine acepta la carga de archivos y el almacenamiento de archivos planos en el servidor de Google a través de db.TextPropertyy db.BlobProperty.

Me alegrará que alguien pueda proporcionar una muestra de código (tanto del lado del cliente como del servidor) sobre cómo se puede hacer esto.

Graviton
fuente
@ user858915 El enlace está roto :(
Marco

Respuestas:

44

Aquí hay un archivo completo y funcional. Saqué el original del sitio de Google y lo modifiqué para hacerlo un poco más real.

Algunas cosas a tener en cuenta:

  1. Este código usa la API BlobStore
  2. El propósito de esta línea en la clase ServeHandler es "arreglar" la clave para que se deshaga de cualquier alteración de nombres que pueda haber ocurrido en el navegador (no observé ninguna en Chrome)

    blob_key = str(urllib.unquote(blob_key))
    
  3. La cláusula "save_as" al final de esto es importante. Se asegurará de que el nombre del archivo no se altere cuando se envíe a su navegador. Deshazte de él para observar qué pasa.

    self.send_blob(blobstore.BlobInfo.get(blob_key), save_as=True)
    

¡Buena suerte!

import os
import urllib

from google.appengine.ext import blobstore
from google.appengine.ext import webapp
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app

class MainHandler(webapp.RequestHandler):
    def get(self):
        upload_url = blobstore.create_upload_url('/upload')
        self.response.out.write('<html><body>')
        self.response.out.write('<form action="%s" method="POST" enctype="multipart/form-data">' % upload_url)
        self.response.out.write("""Upload File: <input type="file" name="file"><br> <input type="submit" name="submit" value="Submit"> </form></body></html>""")

        for b in blobstore.BlobInfo.all():
            self.response.out.write('<li><a href="https://stackoverflow.com/serve/%s' % str(b.key()) + '">' + str(b.filename) + '</a>')

class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        self.redirect('/')

class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, blob_key):
        blob_key = str(urllib.unquote(blob_key))
        if not blobstore.get(blob_key):
            self.error(404)
        else:
            self.send_blob(blobstore.BlobInfo.get(blob_key), save_as=True)

def main():
    application = webapp.WSGIApplication(
          [('/', MainHandler),
           ('/upload', UploadHandler),
           ('/serve/([^/]+)?', ServeHandler),
          ], debug=True)
    run_wsgi_app(application)

if __name__ == '__main__':
  main()
010110110101
fuente
Asombroso que esto funciona para todos, excepto para mí ... self.__uploadsen blobstore_handleres Nonecuando intento esto.
Tim
49

De hecho, esta pregunta se responde en la documentación de App Egnine. Vea un ejemplo sobre la carga de imágenes de usuario .

Código HTML, dentro de <form> </form>:

<input type = "file" name = "img" />

Código Python:

class Libro de visitas (webapp.RequestHandler):
  def post (yo):
    saludo = Saludo ()
    si users.get_current_user ():
      greeting.author = users.get_current_user ()
    greeting.content = self.request.get ("contenido")
    avatar = self.request.get ("img")
    greeting.avatar = db.Blob (avatar)
    greeting.put ()
    self.redirect ('/')
Sastanin
fuente
No me gusta este enfoque porque pierde información de tipo mimo.
santiagobasulto
@santiagobasulto: ¿Por qué no lo revisa usted mismo?
vietnamita
No quiero comprobarlo. Cuando tenga que mostrar una imagen, debe proporcionar información de tipo mime (si la imagen es JPG, GIF, etc.), por lo que debe proporcionar el encabezado HTTP del tipo de contenido. Si carga la imagen con la solución que proporciona, no sabrá en el futuro el tipo de contenido de la imagen, ergo, no puede configurar el encabezado, ergo, los navegadores antiguos tendrán problemas para mostrar la imagen ( debido a la ausencia del tipo de contenido)
santiagobasulto
1
Si no marca el tipo de mime, está confiando en el cliente, dejándolo abierto a ataques de sombrero negro o tipos de mime mal configurados en el cliente. Si está a punto de hacer eso, puede confiar en la extensión del archivo.
Nacho Coloma
10

Hay un hilo en Grupos de Google al respecto:

Carga de archivos

Con mucho código útil, esa discusión me ayudó mucho a subir archivos.

Peter Mortensen
fuente
6

Google ha lanzado un servicio para almacenar archivos grandes. Eche un vistazo a la documentación de la API de blobstore . Si sus archivos tienen más de 1 MB, debe usarlos.

jbochi
fuente
Cómo guardar archivos> 1mb. Estoy usando github.com/ckopanos/django-google-cloud-storage
Geo Jacob
6

Lo intento hoy, funciona de la siguiente manera:

mi versión sdk es 1.3.x

página html:

<form enctype="multipart/form-data" action="/upload" method="post" > 
<input type="file" name="myfile" /> 
<input type="submit" /> 
</form> 

Código del servidor:

file_contents = self.request.POST.get('myfile').file.read() 
Bili
fuente
3

Si sigues teniendo problemas, comprueba que estás usando enctype en la etiqueta del formulario.

No:

<form encoding="multipart/form-data" action="/upload">

Si:

<form enctype="multipart/form-data" action="/upload">
Joe Petrini
fuente
Recibí un error de codificación antes de implementar su respuesta
Jader Dias
1
Un problema realmente molesto para mí cuando estaba haciendo esto fue no incluir el "tamaño" para el tipo de entrada del archivo. Estaba probando con Safari, que aparentemente tiene una longitud de archivo muy corta por defecto (?) Y todo lo que obtenía en GAE para el contenido del archivo era el nombre del archivo. Solo una advertencia que podría ahorrarle a alguien un pequeño dolor de cabeza.
John Carter
1

No puede almacenar archivos porque no existe un sistema de archivos tradicional. Solo puede almacenarlos en su propio DataStore (en un campo definido como BlobProperty )

Hay un ejemplo en el enlace anterior:

class MyModel(db.Model):
  blob = db.BlobProperty()

obj = MyModel()
obj.blob = db.Blob( file_contents )
Guido
fuente
1

Personalmente, el tutorial que se describe aquí me resultó útil cuando se usa el tiempo de ejecución de Java con GAE. Por alguna razón, cuando intenté cargar un archivo usando

<form action="/testservelet" method="get" enctype="multipart/form-data">
    <div>
        Myfile:<input type="file" name="file" size="50"/>
    </div>

    <div>
        <input type="submit" value="Upload file">
    </div>
</form>

Descubrí que mi clase HttpServlet por alguna razón no aceptaba el formulario con el atributo 'enctype'. Eliminarlo funciona, sin embargo, esto significa que no puedo cargar ningún archivo.

Peter Mortensen
fuente
1
Puede deberse a que está utilizando el método get, intente configurarlo para publicar en su lugar. No estoy seguro de si funcionará, pero vale la pena intentarlo.
slashnick
0

He observado un comportamiento extraño al cargar archivos en App Engine. Cuando envía el siguiente formulario:

<form method="post" action="/upload" enctype="multipart/form-data">
    <input type="file" name="img" />
    ...
</form>

Y luego extrae el imgde la solicitud de esta manera:

img_contents = self.request.get('img')

La img_contentsvariable es una str()en Google Chrome, pero es unicode en Firefox. Y como ahora, el db.Blob()constructor toma una cadena y arrojará un error si pasa una cadena Unicode.

¿Alguien sabe cómo se puede solucionar esto?

Además, lo que encuentro absolutamente extraño es que cuando copio y pego la aplicación Libro de visitas (con avatares), funciona perfectamente. Hago todo exactamente de la misma manera en mi código, pero simplemente no funcionará. Estoy muy cerca de arrancarme el pelo.

Honza Pokorny
fuente
2
: D El formulario decía: mutlipart / form-data en lugar de multipart / form-data. Chrome es lo suficientemente inteligente como para corregir el error tipográfico, Firefox no lo es.
Honza Pokorny
0

Hay una forma de usar el sistema de archivos planos (al menos en perspectiva de uso)

Existe este proyecto de sistema de archivos virtual de Google App Engine . que se implementa con la ayuda del almacén de datos y las API de Memcache para emular un sistema de archivos normal. Usando esta biblioteca, puede usar en su proyecto un acceso al sistema de archivos similar (lectura y escritura) .

vivek_jonam
fuente