在单个请求中下载多个文件 (jQuery/Python)
Posted
技术标签:
【中文标题】在单个请求中下载多个文件 (jQuery/Python)【英文标题】:Download Multiple Files in a Single Request (jQuery/Python) 【发布时间】:2011-07-26 08:28:46 【问题描述】:我需要为用户创建一种打开网页的方式,从表单中的复选框列表中进行选择,然后在提交表单后,将所有这些文件一起下载。
以下是客户对我施加的限制:
平台主要是移动设备 没有 zip 文件(因为我们不能假设移动设备可以处理 zip) 文件必须下载,而不是流式传输因此,我在前端使用带有 jQuery Mobile 1.0a3 的 Xhtml/CSS 和在后端使用带有 Python 2.6 的 Apache 创建了一个 Web 应用程序。将下载的目标文件是 .mp3 文件。
我已经成功地使用从服务器传递并由 jQuery 通过 AJAX 加载的隐藏 iframe 在桌面上执行了所需的效果......但它在默认的 android 浏览器或 Dolphin 浏览器上不起作用。
我确保我的 Apache 配置会强制执行下载行为:
<Files *.mp3>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>
此外,Apache 的“headers”模块已启用(需要“Header set”配置参数),所以这不是问题。
我使用所有选定的项目作为参数对服务器进行 AJAX 调用,当服务器读取项目数组时,它将查询数据库以获取有关每个项目的信息(例如每个 mp3 文件的 URL) .然后在后端为每个 mp3 文件创建 iframe 代码,然后发送回 jQuery 的$.load()
函数以加载新的 iframe(下载 mp3)。
不用粘贴太多代码,这是我正在做的一个非常简短的测试用例:
服务器端
def download(req):
resultDiv = """<div id="downloads">"""
queryIds = []
for element in req.form:
# "element" contains the id number that matches database record
trackId = re.match('^track(\d+)', element).group(1)
queryIds.append(trackId)
conn = mysqldb.connect(host='localhost', user='fake', passwd='fake', db='fake')
cursor = conn.cursor()
buildQuery = """\
SELECT filePath FROM tracks
WHERE trackNum in ("""
buildQuery += ','.join(queryIds)
buildQuery += ')'
cursor.execute(buildQuery)
downloadRows = cursor.fetchall()
for track in downloadRows:
resultDiv += """
<iframe src="%s"></iframe>
""" % track[0]
return resultDiv
客户端
<!DOCTYPE html>
<html>
<head>
<!-- INCLUDES FOR JQUERY MOBILE AND JQUERY -->
<style type="text/css">
.invisible
display: none;
</style>
<script type="text/javascript">
$(document).ready(function()
$('#albumForm').submit(function(e)
e.preventDefault();
// this will hold the selected items on the form
selTracks = ;
$('#trackList').find(':checked').each(function()
selTracks[this.id] = 'on';
);
// load the iframes into a 'div' set aside for that purpose
$('#results').load('control.py/download #tracks', selTracks);
);
);
</script>
</head>
<body>
<div data-role='page' id='page'>
<div data-role='header' id='header'>
</div>
<div data-role='content' id='content'>
<div id='container'>
<form id='albumForm'>
<div data-role='controlgroup' data-role='fieldcontain'>
<input type='checkbox' name='track1' id='track1' />
<label for='track1' id='track1label'>Track 1</label>
<input type='checkbox' name='track2' id='track2' />
<label for='track2' id='track2label'>Track 2</label>
<input type='checkbox' name='track3' id='track3' />
<label for='track3' id='track3label'>Track 3</label>
<input type='submit' id='downloadButton' value='Download' />
</div>
</form>
</div>
<div id='results' class='invisible'>
</div>
</div>
<div data-role='footer' id='footer'>
</div>
</div>
</body>
</html>
抱歉,代码太通用了(而且大大缩短了),但我无权发布实际代码(你知道它是怎么回事)。但这基本上是它的要点;我相信问题出在移动浏览器的解释中,或者可能在某处的 HTTP 标头中?这在桌面上的 Chrome 和 Firefox 中工作,它实际上在 Fennec for Android 中完全按照预期工作(它下载所有文件而无需进一步交互,只是在通知栏中显示它们)。我只是不能假设每个人都在使用 Fennec(他们不是,哈哈)。
除了上述之外,我还尝试了以下方法(都可以在台式机上运行,但不能在移动设备上运行):
从服务器返回的 JSON,以及 jQuery 在客户端创建的 iframe 从服务器返回的 JSON,以及为每个 URL 调用window.open()
的 for 循环
从服务器返回的 JSON,以及由 jQuery 创建的 <a>
标签和触发的 click()
使用不同的 DOCTYPE
以下是我尝试过的在台式机或移动设备上都不起作用的方法:
修改location.href
或window.location
(显然只能做一次)
在服务器上调用req.sendfile()
(也许我做错了?)
返回多部分/表单数据并从服务器转储具有设定边界的二进制数据(非常混乱,也许我也做错了?)
仍然没有喜悦;我可能会错过什么?
附:请不要因为我使用隐藏的 iframe 而激怒我...
编辑:我什至可以使用可以在服务器上设置的另一个本机浏览器协议,例如 FTP。欢迎所有想法。
更新:我正在尝试启动从客户端到服务器的 FTP 连接并运行“mget”。我知道 net2ftp 可以做到这一点……现在来弄清楚 ;) 仍然有新的想法。
【问题讨论】:
由于您实际上不需要在<iframes>
中填充任何内容,您是否尝试过使用<script>
标签而不是<iframe>
标签?您可能还想在 URL 的末尾添加一个 nonce 参数以防止缓存问题。
这不起作用...它正确加载了<script>
标签,但没有下载文件。我怀疑它对待 <script>
标记很像典型的 JS 文件:非持久性。
嗯。哦,好吧,很抱歉浪费你的时间(但如果是我,我也会尝试的 :-)
@Pointy:别担心,不要浪费时间!我花了大约 5 秒钟的时间来尝试;)
【参考方案1】:
这是一个客户端限制。每个域有一个并行下载限制。尝试提供来自不同域的文件,例如 d1.example.com 和 d2.example.com。它们都可以由同一个虚拟主机提供服务:
<VirtualHost *:80>
ServerName example.com
ServerAlias d1.example.com d2.example.com
...
</VirtualHost>
【讨论】:
这是专门针对 Android 浏览器的客户端限制吗?因为它可以在桌面上正常运行,也可以在 Android (Fennec) 上运行 Firefox for Mobile。它只是无法在默认的 Android 浏览器或 Dolphin 浏览器上运行。 @JoeLinux 每个浏览器都有自己的限制。 好的,我添加了从不同但顺序的子域下载每个曲目的代码......仍然无法在移动浏览器上运行。它继续在桌面上工作。以上是关于在单个请求中下载多个文件 (jQuery/Python)的主要内容,如果未能解决你的问题,请参考以下文章
我们可以在 Android Kotlin 的单个协程中下载多个文件吗
分享一个FileUtil工具类,基本满足web开发中的文件上传,单个文件下载,多个文件下载的需求