Prześlij pliki w Google App Engine


79

Planuję stworzyć aplikację internetową, która umożliwi użytkownikom obniżanie wersji plików projektów Visual Studio. Wygląda jednak na to, że Google App Engine akceptuje przesyłanie plików i przechowywanie prostych plików na serwerze Google za pośrednictwem db.TextPropertyi db.BlobProperty.

Cieszę się, że każdy może dostarczyć przykładowy kod (zarówno po stronie klienta, jak i po stronie serwera), w jaki sposób można to zrobić.


@ user858915 Link jest uszkodzony :(
Marco

Odpowiedzi:


44

Oto kompletny, działający plik. Wyciągnąłem oryginał z witryny Google i zmodyfikowałem go, aby był nieco bardziej prawdziwy.

Kilka rzeczy, na które należy zwrócić uwagę:

  1. Ten kod używa interfejsu API BlobStore
  2. Celem tej linii w klasie ServeHandler jest "naprawienie" klucza tak, aby pozbył się wszelkich zmian nazw, które mogły wystąpić w przeglądarce (nie zauważyłem żadnego w Chrome)

    blob_key = str(urllib.unquote(blob_key))
    
  3. Klauzula „save_as” na końcu tego jest ważna. Zapewni to, że nazwa pliku nie zostanie zniekształcona, gdy zostanie wysłany do przeglądarki. Pozbądź się go, aby obserwować, co się dzieje.

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

Powodzenia!

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()

Niesamowite, że działa to dla wszystkich z wyjątkiem dla mnie ... self.__uploadsw blobstore_handlerto None, kiedy próbuję to.
Tim

49

W rzeczywistości odpowiedź na to pytanie znajduje się w dokumentacji App Egnine. Zobacz przykład przesyłania obrazów użytkowników .

Kod HTML wewnątrz <form> </form>:

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

Kod Pythona:

class Księga gości (webapp.RequestHandler):
  def post (self):
    powitanie = Powitanie ()
    if users.get_current_user ():
      pozdrowienia.author = users.get_current_user ()
    powitanie.content = self.request.get ("zawartość")
    avatar = self.request.get („img”)
    pozdrowienia.avatar = db.Blob (avatar)
    powitanie.put ()
    self.redirect („/”)

Nie podoba mi się to podejście, ponieważ tracisz informacje o typie MIME.
santiagobasulto

@santiagobasulto: Dlaczego nie sprawdzisz tego samodzielnie?
vietean

Nie chcę tego sprawdzać. Kiedy musisz wyświetlić obraz, musisz podać informacje o typie MIME (czy obraz jest w formacie JPG, GIF itp.), Więc musisz podać nagłówek HTTP typu treści. Jeśli prześlesz obraz z rozwiązaniem, które podasz, nie będziesz wiedział w przyszłości typu treści zdjęcia, ergo, nie możesz ustawić nagłówka, ergo, stare, dziwne przeglądarki będą miały problemy z wyświetleniem obrazu ( ze względu na brak typu treści)
santiagobasulto

1
Jeśli nie zaznaczysz typu MIME, któremu ufasz klientowi, będziesz narażony na ataki czarnego kapelusza lub źle skonfigurowane typy MIME w kliencie. Jeśli masz zamiar to zrobić, możesz po prostu zaufać samemu rozszerzeniu pliku.
Nacho Coloma



6

Dziś próbuję, działa następująco:

moja wersja sdk to 1.3.x

strona html:

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

Kod serwera:

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

3

Jeśli nadal masz problem, sprawdź, czy używasz enctype w tagu formularza

Nie:

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

Tak:

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

Wystąpił błąd kodowania, zanim zaimplementowałem twoją odpowiedź
Jader Dias,

1
Naprawdę irytującą przeszkodą było dla mnie, gdy to robiłem, nie uwzględnianie „rozmiaru” dla typu pliku wejściowego. Testowałem z Safari, która najwyraźniej ma domyślnie bardzo krótką długość pliku (?), A wszystko, co otrzymałem w GAE dla zawartości pliku, to nazwa pliku. Tylko słowo ostrzeżenia, które może zaoszczędzić komuś drobnego bólu głowy.
John Carter

1

Nie można przechowywać plików, ponieważ nie ma tradycyjnego systemu plików. Możesz je przechowywać tylko w ich własnym DataStore (w polu zdefiniowanym jako BlobProperty )

W poprzednim linku znajduje się przykład:

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

obj = MyModel()
obj.blob = db.Blob( file_contents )

1

Osobiście uważam, że samouczek opisany tutaj jest przydatny podczas używania środowiska wykonawczego Java z GAE. Z jakiegoś powodu, gdy próbowałem przesłać plik przy użyciu

<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>

Okazało się, że moja klasa HttpServlet z jakiegoś powodu nie akceptuje formularza z atrybutem „enctype”. Usunięcie go działa, jednak oznacza to, że nie mogę przesłać żadnych plików.


1
Może tak być, ponieważ używasz metody get, spróbuj zamiast tego ustawić ją na post. Nie jestem pewien, czy to zadziała, ale warto spróbować.
slashnick


0

Zauważyłem dziwne zachowanie podczas przesyłania plików do App Engine. Przesyłając następujący formularz:

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

A następnie wyodrębniasz imgz żądania w ten sposób:

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

img_contentsZmienna jest str()w Google Chrome, ale to unicode w Firefoksie. I tak jak teraz, db.Blob()konstruktor pobiera ciąg znaków i zgłasza błąd, jeśli przekażesz łańcuch znaków Unicode.

Czy ktoś wie, jak można to naprawić?

Co więcej, wydaje mi się absolutnie dziwne, że kiedy kopiuję i wklejam aplikację Księga gości (z awatarami), działa idealnie. Robię wszystko dokładnie tak samo w swoim kodzie, ale to po prostu nie zadziała. Jestem już blisko wyrywania włosów.


2
: D Formularz powiedział: mutlipart / form-data zamiast multipart / form-data. Chrome jest wystarczająco sprytny, aby poprawić literówkę, Firefox nie.
Honza Pokorny

0

Istnieje sposób wykorzystania płaskiego systemu plików (przynajmniej w perspektywie użytkowej)

Jest to projekt wirtualnego systemu plików Google App Engine . który jest implementowany przy pomocy interfejsów API datastore i memcache w celu emulacji zwykłego systemu plików. Korzystając z tej biblioteki, możesz użyć w swoim projekcie podobnego dostępu do systemu plików (odczyt i zapis) .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.