【问题标题】:How to save uploaded file and then return that with GAE?如何保存上传的文件,然后用 GAE 返回?
【发布时间】:2011-07-27 18:16:56
【问题描述】:

我需要使用 GAE 创建以下应用程序:

  1. 用户上传了一些文件(比如说 POST 到 myapp.appspot.com/upload);
  2. 我需要保存它(在数据存储中?)并返回链接;
  3. 根据提供的链接,用户应该能够在接下来的 5 分钟内下载文件。

我创建了以下内容:

app.yaml

application: synoext
version: 1
runtime: python
api_version: 1

handlers:
- url: /upload
  script: synoext.py

- url: /file/\w+
  script: synoext.py

- url: /cleanup
  script: synoext.py

synoext.py

import datetime
import logging
import urlparse

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.api import urlfetch

class Files(db.Model):
    file = db.BlobProperty()
    added = db.DateTimeProperty(auto_now_add=True)  

class UploadFile(webapp.RequestHandler):
    def post(self):
        logging.info('(POST) Uploading new file')
        # saving file in the database
        file = Files()
        file.file = db.Blob(self.request.get("file"))
        file.put()

        self.response.out.write('http://myapp.appspot.com/' + str(file.key()))

class GetFile(webapp.RequestHandler):
    def get(self, key):
        file = db.get(key)
        if file is not None:
            self.response.headers['Content-Type'] = 'application/x-bittorrent'
            self.response.out.write(file.file)
        else:
            self.response.set_status(404)

class Cleanup(webapp.RequestHandler):
    def get(self):
        '''Automatically run job (cron) to delete old records (maximum 10000)
        from Files database (records, which are older than 5 minutes)
        '''
        fiveMinutesAgoDate = datetime.datetime.now() - datetime.timedelta(minutes=5)

        q = db.GqlQuery("SELECT * FROM Files WHERE added < :1", fiveMinutesAgoDate)
        results = q.fetch(10000)
        db.delete(results)

        self.response.out.write('{"result": true}')


application = webapp.WSGIApplication(
                                     [('/upload', UploadFile),
                                      ('/file/(\w+)', GetFile),
                                      ('/cleanup', Cleanup)],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

正确吗?方法是否正确?或者,我不应该使用数据存储吗?

更新。奇怪,但是下面的代码

def get(self, key):
    file = db.get(key)
    if file is not None:

如果使用了不正确的密钥,则无法正常工作。 这里有什么问题?

【问题讨论】:

    标签: google-app-engine memcached google-cloud-datastore blobstore


    【解决方案1】:

    粗略查看一下代码,您的方法应该可行。但是,您可能希望使用 Blobstore 而不是数据存储中的 blob,具体取决于您的需求和您尝试提供的文件的大小。

    【讨论】:

    • 使用 blobstore 有什么好处?文件约为 20Kb。
    • 如果您的文件为 20kb,则坚持使用数据存储。这是 blobstore 与数据存储的快速概述。 blobstore 的优点: 1)您可以存储更大的文件;每个数据存储实体限制为 1MB。 2) 更便宜; blobstore 配额大约比高复制数据存储便宜 3 倍。但是,缺点:1)您必须启用计费;您可以获得 1GB 的免费空间,但您的应用必须启用它才能使用 blobstore。 2) Blobstore 不支持命名空间。
    • 命名空间或多或少与 blobstore 无关,因为它发出不透明的密钥 - 只需确保将密钥存储在相关命名空间中的数据存储实体中。
    • 我不应该改用memcache吗?然后我将能够避免使用该 cron-cleanup 作业。
    • 我不清楚你的意思,什么 cron 清理工作?你还需要内存缓存做什么? Memcache 就是一个缓存。如果您说将其用作文件的唯一存储,那是一个非常非常糟糕的主意。
    【解决方案2】:

    由于存储的文件非常小,您绝对应该像您正确的那样使用datastore

    一些建议:

    1. 由于您只需要删除键,因此您应该只查询带有SELECT __key__ FROM Files WHERE .. 的键,从而节省一些资源。

    2. 如果文件数量很大,您可以使用mapper-api 删除所有条目;您可以使用 control api 从您的代码中启动 mapreduce 作业。

    3. /file/(\w+) 无法捕获 url applications_- 的每个 base64 编码键都是有效字符,您也应该将它们与类似 /file/([\w_-]+) 的内容匹配

    【讨论】:

    • Cron 作业与任何其他离线任务具有相同的 10 分钟时间限制 - 10 分钟。不过,使用 ETA 将单个任务排入队列以删除每个 blob 将是一个更简洁的解决方案。
    • @Nick 哦,很高兴知道,我不知道谢谢。我一直认为 cron-jobs 调用是面向用户的请求。关于评论的第二部分,您的意思是比使用 mapper-api 更好?
    • Mapreduce 适合批量更新,但在这里我们确切地知道哪些记录需要更新。为每条记录排队一个任务,即删除该记录,这很简单,应该可以正常工作。
    • ...虽然我应该补充一点,如果你不经常运行 mapreduce - 例如每天 - 绝大多数记录都需要删除,所以它会很有效。
    • @Nick 5 分钟上传文件是一个小窗口,我同意该任务可以由“规范”任务队列处理,但我对此很好奇,你为什么建议一一删除密钥?批量删除不是更方便更省资源吗?
    猜你喜欢
    • 2013-02-21
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-17
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多