web2py:下载由控制器中的 cStringIO 文本字符串创建的 zip 文件

web2py : downloading zip file created by cStringIO text strings in controller

我有一个基于 web2py 的门户网站,其中一组注释者为显示给他们的各种图像提供文本标签和边界框相关信息。我想以 XML 文件(每个图像一个)的形式获取此信息,并向门户添加下载注释功能,该门户提供包含所有这些 XML 文件的 ZIP 文件。我可以从门户网站创建一个 zip 下载,但解压缩它会引发以下错误:

mohit@nightfury13:~/Downloads$ unzip Arabic\ Set1-Arabic_annotations.zip    
Archive:  Arabic Set1-Arabic_annotations.zip
      End-of-central-directory signature not found.  Either this file is not
      a zipfile, or it constitutes one disk of a multi-part archive.  In the
      latter case the central directory and zipfile comment will be found on
      the last disk(s) of this archive.
    unzip:  cannot find zipfile directory in one of Arabic Set1-Arabic_annotations.zip or
            Arabic Set1-Arabic_annotations.zip.zip, and cannot find Arabic Set1-Arabic_annotations.zip.ZIP, period.

以下是我为执行此任务而编写的代码。有人可以指出我做错了什么吗? - 代码的前半部分是数据集-XML-字符串的准备(您可以跳过)。下半部分是 zip 管道。

@auth.requires_login()
def prepare_dataset():
    import os
    from PIL import Image
    import zipfile, cStringIO

    # Check if a valid data-id was passed.
    if not request.vars.data_id:
        session.flash = 'No dataset selected for download'
        redirect(URL('default', 'select_db?redirect=1'))

    # Create the annotation-data in a proper format.
    else:
        data_id = int(request.vars.data_id)
        dataset = db(db.Datasets.id==data_id).select()[0]
        root_path = dataset['data_path'].split('cropped')[0]
        root_images = [i for i in os.listdir(root_path) if i.endswith('.jpg') or i.endswith('.jpeg') or i.endswith('.png')]
        content = {}
        imgs_data = db((db.Images.data_id==data_id)&(db.FinalLabels.img_id==db.Images.id)).select()
        for img_data in imgs_data:
            label = img_data['FinalLabels']['label']
            if 'bad' not in [i.lower() for i in label.split()]:
                img_name = img_data['Images']['img_name']
                root_img_name = img_name.split('_')[0]
                xmin, ymin, xmax, ymax = img_name.split('.')[0].split('_')[2:]

                if root_img_name not in content:
                    r_im_name = [i_name for i_name in root_images if root_img_name in i_name][0] # This one also has the extension
                    root_im = Image.open(os.path.join(root_path, r_im_name))
                    r_depth = 3
                    if not root_im.mode=='RGB':
                        r_depth = 1
                    r_width, r_height = root_im.size

                    content[root_img_name] = {'name':r_im_name, 'depth':r_depth, 'width':r_width, 'height':r_height, 'crops':[{'label':label, 'xmin':xmin, 'ymin':ymin, 'xmax':xmax, 'ymax':ymax}]}
                else:
                    content[root_img_name]['crops'].append({'label':label, 'xmin':xmin, 'ymin':ymin, 'xmax':xmax, 'ymax':ymax})

        # Compress img-annotation data (content) to zip and export
        zip_chunks = cStringIO.StringIO()
        zipf = zipfile.ZipFile(zip_chunks, "w", compression=zipfile.ZIP_DEFLATED)

        for im_name in content:
            root_im = content[im_name]
            root_folder = filter(None, root_path.split('/'))[-1]
            xml_str = """<annotation>
    <folder>%s</folder>
    <filename>%s</filename>
    <path>%s</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>%s</width>
        <height>%s</height>
        <depth>%s</depth>
    </size>
    <segmented>0</segmented>""" % (root_folder, im_name, root_im['name'], root_im['width'], root_im['height'], root_im['depth'])

            for crop in root_im['crops']:
                xml_str+="""
    <object>
        <name>%s</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>%s</xmin>
            <ymin>%s</ymin>
            <xmax>%s</xmax>
            <ymax>%s</ymax>
        </bndbox>
    </object>""" % (crop['label'], crop['xmin'], crop['ymin'], crop['xmax'], crop['ymax'])


            xml_str+='\n</annotation>'
            zipf.writestr(im_name+'.xml', xml_str)

        zipf.close()
        zip_name = dataset['data_name']+'_annotations.zip'
        file_header = 'attachment; filename='+zip_name
        response.headers['Content-Type'] = 'application/zip'
        response.headers['Content-Disposition'] = file_header
        return zipf

ZipFile 正在将数据写入 zip_chunks StringIO 对象,因此您必须 return zip_chunks.getvalue() 而不是 zipf.