在单个请求中下载多个文件(jQuery / Python)

时间:2011-03-30 19:15:22

标签: jquery ajax apache jquery-mobile mod-python

嘿伙计们。基本上,我需要为用户创建一种打开网页的方式,从表单中的复选框列表中进行选择,并在提交表单后,将所有这些文件一起下载。

以下是客户对我施加的限制:

  • 平台主要是移动设备
  • 没有zip文件(因为我们不能假设移动设备可以处理拉链)
  • 文件必须下载,而不是流式传输

所以我在前端使用XHTML / CSS w / jQuery Mobile 1.0a3,在后端使用Apache w / Python 2.6创建了一个Web应用程序。将下载的目标文件是.mp3文件。

我已成功设法使用从服务器传递并通过AJAX通过jQuery加载的隐藏iframe在桌面上执行所需效果...但它无法在默认的Android浏览器或Dolphin浏览器上运行。

我确保我的Apache配置会强制下载行为:

<Files *.mp3>
    ForceType application/octet-stream
    Header set Content-Disposition attachment
</Files>

此外,Apache的“标题”模块已启用(“标头集”配置参数所需),因此这不是问题。

我使用所有选定的项目作为参数对服务器进行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.hrefwindow.location(显然只能执行一次)
  • 在服务器上调用req.sendfile()(也许我做错了?)
  • 返回multipart / form-data并从服务器转储带有设定边界的二进制数据(非常混乱,也许我也做错了?)

仍然没有快乐;我能错过什么?

P.S。请不要因为使用隐藏的iframe而激怒我......

编辑:我甚至可以使用我可以在服务器上设置的另一个本机浏览器协议,例如FTP。欢迎所有的想法。

更新:我尝试启动从客户端到服务器的FTP连接并运行“mget”。我知道net2ftp可以做到这一点......现在想出来了;)仍然需要新想法。

1 个答案:

答案 0 :(得分:0)

这是客户限制。每个域并行下载限制。尝试提供来自不同域的文件,例如d1.example.com和d2.example.com。它们都可以从同一个虚拟主机提供:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias d1.example.com d2.example.com
    ...
</VirtualHost>