我正在创建CKAN插件,以允许用户从数据库服务器选择和下载多个资源(文件)。 我用一些Python代码创建了flask视图函数,该函数应使用请求的资源创建一个zip文件,但是即使给出了绝对路径,创建也会失败,并显示“无此类文件或目录”错误。
该代码基于this,this和this的帖子,为了解决当前问题,我已经使用以下方法检查了here和here没有太大的成功。
代码如下:
import zipfile
import io
def download_multiple_resources():
if request.method == "POST":
memory_file = io.BytesIO()
with zipfile.ZipFile(memory_file, "w", zipfile.ZIP_STORED) as zf:
for res in request.form.values():
zf.write(str(res), 'download.zip')
return send_file(memory_file, mimetype='application/zip', as_attachment=True, attachment_filename='download.zip')
用户通过发布请求的表单选择资源。
该表单将提交所请求文件的完整路径,因此,如果用户选择下载testfile.txt
,则在res
值上方的代码中,http://localhost:5000/dataset/6192bb3b-6c4a/resource/dbe39d59-b938/download/testfile.txt
类似于。 (当前正在我的计算机上的Docker容器中创建的服务器上进行测试)
请注意,将上述路径复制粘贴到浏览器中后,我可以毫无问题地访问文件。
但是,zf.write
命令失败,并显示“无此类文件或目录”错误。
有什么问题的主意吗?
答案 0 :(得分:0)
您说要通过资源的绝对路径访问资源,但是res
的示例值是URL。
我不认为zipfile.ZipFile.write
支持这一点,因为将文件添加到zip归档文件会使用文件系统中的元数据(时间戳等)。您将需要通过资源的本地路径访问资源,或者,如果无法从应用程序容器访问资源,请先将其下载到临时位置。 (当然,后一种情况会删除元数据。)
答案 1 :(得分:0)
结果证明,我最初的猜测是正确的,除了通过http请求读取文件之外,只需使用writestr()
而不是write()
。
这是完整的工作代码:
import zipfile
import io
import requests
from flask import request, send_file
def download_multiple_resources():
if request.method == "POST":
memory_file = io.Bytes.IO()
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_STORED) as zf:
for res in request.form:
f = requests.get(res, allow_redirects=True)
zf.writestr('your_chosen_filename', f.content)
memory_file.seek(0)
return send_file(memory_file, mimetype='application/zip', as_attachment=True, attachment_filename="download.zip")
特别注意:如果您正在像我这样的外部服务器上工作,并使用此功能让已登录的用户下载文件,请确保将用户的凭据传递给request.get()
调用授权下载。就我而言,这是使用选项headers={'Authorization': user.apiKey}
完成的,但这可能取决于您的框架/应用程序。