如何为动态生成的 google 云存储对象获取有效的 blobstore 密钥?
How do I get a valid blobstore key for a google cloudstorage object that is generated dynamically?
我有一个用例,我需要动态生成一个 csv/txt 文件,然后将文件的密钥保存到数据存储区,以便稍后可以下载该文件。我似乎在生成与 Ferris 的下载 uri 结合使用的有效密钥时遇到问题。
例如:
import cloudstorage
from google.appengine.ext import blobstore
@route
def make_file(self):
# Hardcoded filename, this will overwrite prior file if it exists
filename = '/mydomain.appspot.com/some_folder/myawesometextfile2.txt'
# Create file
gcs_file = cloudstorage.open(filename,'w',content_type='text/plain')
# Generate the file's contents (pretend this is being done dynamically)
gcs_file.write('Doe,John\n')
gcs_file.write('Smith,Jane\n')
# Close the file
gcs_file.close()
# This is supposed to create a blobkey that represents the cloud object
psuedo_blobkey = blobstore.create_gs_key('/gs'+filename)
# This is supposed to also create a blobkey...I think?
another_key = blobstore.BlobKey(psuedo_blobkey)
# Here I attempt to store this in the datastore.
new_file = self.meta.Model(
file_key = another_key,
file_name_actual = filename,
)
new_file.put()
如果我尝试将 "psuedo_blobkey" 保存到我的 NDB 模型中,我会收到类似 "Expected BlobKey, got AMIfv-yadda-yadda-yadda".
的错误
如果我尝试将 "another_key" 保存到我的模型中,它会毫无问题地存储密钥,但是当我尝试通过 appengine 仪表板中的数据存储查看器访问该实体时,它告诉我它不是有效的钥匙。因此,当我尝试像这样在我的神社模板中使用密钥时:
<a href="{{uri("download", blob=file_key)}}" target="_blank">Export</a>
Ferris 呈现错误 "The resource could not be found." 这是有道理的,因为它显然不是有效的密钥。
所以我想我的问题是,我究竟如何获得在 google 云存储中动态生成的文件的有效密钥?
顺便说一句:通过上传操作获取密钥很容易,但由于某些原因,动态生成的 GCS 对象不会产生相同的结果。
提前致谢
我存储文件名而不是密钥。我存储的文件不是很大,所以在我的应用引擎代码中,我是这样读取它们的:
def get_gcs(fn):
"Slurps a GCS file and returns it as unicode."
try:
with gcs.open(fn, "r") as f:
return f.read(32*1024*1024).decode("utf-8")
except gcs.NotFoundError:
return ""
然后我使用标准应用引擎技术将它们提供给用户。如果你的文件很小,这很好用,但如果你的文件很大,可能有更好的方法。
由于您是通过 App Engine 获取文件,因此即使 ACL 设置为私有也能正常工作。
OK 在阅读 this post 并修改 Kekito 的示例后终于弄明白了。
最终我想做的是允许最终用户从 NDB 数据存储中导出数据。我原本以为我必须生成一个文件,将其放入 google 云存储 (GCS) 或 blobstore,然后提供下载 URL。我发现它比那简单得多。我什至认为您不需要在 GCS 中创建一个文件来执行此操作(即使我的示例确实向 GCS 添加了一个文件)。
我所要做的就是在返回文件之前将 Content-Disposition 添加为附件。这会生成另存为对话框,允许用户确定下载位置。如果没有 content-disposition,CSV/TXT 内容会在浏览器上呈现,如果用户试图在浏览器中进行另存为,唯一的选择是 HTML 并且无法更改(在 Chrome 无论如何)。
import csv
import cloudstorage
@route
def export_users(self):
# This can be any NDB query
user_data = self.Meta.Model.get_users(is_active=True)
# Set the file name
filename = '/mydomain.appspot.com/user_list.csv'
# Create the file
gcs_file = cloudstorage.open(filename,'w',content_type='text/csv')
# Setup the CSV file for writing
writer = csv.writer(gcs_file, delimiter=',')
# Loop thru the query and write each row to the CSV file
for user_info in user_data:
writer.writerow( (str(user_info.user_lname),str(user_info.user_fname)) )
# Close the file
gcs_file.close()
try:
# Open the file for reading
with cloudstorage.open(filename, "r") as the_file:
# This will open the Save-As dialog box so the end-user can choose where to save the file
self.response.headers["Content-Disposition"] = "'attachment'; filename=user_list.csv"
# Push the file to the browser
return the_file.read(32*1024*1024).decode("utf-8")
except cloudstorage.NotFoundError:
return "it failed"
我有一个用例,我需要动态生成一个 csv/txt 文件,然后将文件的密钥保存到数据存储区,以便稍后可以下载该文件。我似乎在生成与 Ferris 的下载 uri 结合使用的有效密钥时遇到问题。 例如:
import cloudstorage
from google.appengine.ext import blobstore
@route
def make_file(self):
# Hardcoded filename, this will overwrite prior file if it exists
filename = '/mydomain.appspot.com/some_folder/myawesometextfile2.txt'
# Create file
gcs_file = cloudstorage.open(filename,'w',content_type='text/plain')
# Generate the file's contents (pretend this is being done dynamically)
gcs_file.write('Doe,John\n')
gcs_file.write('Smith,Jane\n')
# Close the file
gcs_file.close()
# This is supposed to create a blobkey that represents the cloud object
psuedo_blobkey = blobstore.create_gs_key('/gs'+filename)
# This is supposed to also create a blobkey...I think?
another_key = blobstore.BlobKey(psuedo_blobkey)
# Here I attempt to store this in the datastore.
new_file = self.meta.Model(
file_key = another_key,
file_name_actual = filename,
)
new_file.put()
如果我尝试将 "psuedo_blobkey" 保存到我的 NDB 模型中,我会收到类似 "Expected BlobKey, got AMIfv-yadda-yadda-yadda".
的错误如果我尝试将 "another_key" 保存到我的模型中,它会毫无问题地存储密钥,但是当我尝试通过 appengine 仪表板中的数据存储查看器访问该实体时,它告诉我它不是有效的钥匙。因此,当我尝试像这样在我的神社模板中使用密钥时:
<a href="{{uri("download", blob=file_key)}}" target="_blank">Export</a>
Ferris 呈现错误 "The resource could not be found." 这是有道理的,因为它显然不是有效的密钥。
所以我想我的问题是,我究竟如何获得在 google 云存储中动态生成的文件的有效密钥?
顺便说一句:通过上传操作获取密钥很容易,但由于某些原因,动态生成的 GCS 对象不会产生相同的结果。
提前致谢
我存储文件名而不是密钥。我存储的文件不是很大,所以在我的应用引擎代码中,我是这样读取它们的:
def get_gcs(fn):
"Slurps a GCS file and returns it as unicode."
try:
with gcs.open(fn, "r") as f:
return f.read(32*1024*1024).decode("utf-8")
except gcs.NotFoundError:
return ""
然后我使用标准应用引擎技术将它们提供给用户。如果你的文件很小,这很好用,但如果你的文件很大,可能有更好的方法。
由于您是通过 App Engine 获取文件,因此即使 ACL 设置为私有也能正常工作。
OK 在阅读 this post 并修改 Kekito 的示例后终于弄明白了。
最终我想做的是允许最终用户从 NDB 数据存储中导出数据。我原本以为我必须生成一个文件,将其放入 google 云存储 (GCS) 或 blobstore,然后提供下载 URL。我发现它比那简单得多。我什至认为您不需要在 GCS 中创建一个文件来执行此操作(即使我的示例确实向 GCS 添加了一个文件)。
我所要做的就是在返回文件之前将 Content-Disposition 添加为附件。这会生成另存为对话框,允许用户确定下载位置。如果没有 content-disposition,CSV/TXT 内容会在浏览器上呈现,如果用户试图在浏览器中进行另存为,唯一的选择是 HTML 并且无法更改(在 Chrome 无论如何)。
import csv
import cloudstorage
@route
def export_users(self):
# This can be any NDB query
user_data = self.Meta.Model.get_users(is_active=True)
# Set the file name
filename = '/mydomain.appspot.com/user_list.csv'
# Create the file
gcs_file = cloudstorage.open(filename,'w',content_type='text/csv')
# Setup the CSV file for writing
writer = csv.writer(gcs_file, delimiter=',')
# Loop thru the query and write each row to the CSV file
for user_info in user_data:
writer.writerow( (str(user_info.user_lname),str(user_info.user_fname)) )
# Close the file
gcs_file.close()
try:
# Open the file for reading
with cloudstorage.open(filename, "r") as the_file:
# This will open the Save-As dialog box so the end-user can choose where to save the file
self.response.headers["Content-Disposition"] = "'attachment'; filename=user_list.csv"
# Push the file to the browser
return the_file.read(32*1024*1024).decode("utf-8")
except cloudstorage.NotFoundError:
return "it failed"