Django 从内存中提供压缩的 GeoDataFrame shapefile 作为下载

Hei*_*ich 2 python django zip geopandas

我有一个与 Django GIS 相关的应用程序,用户可以在其中下载 shp 文件。我有 geopandas GeoDataFrame 对象。我可以轻松地将其转换为 zip 文件,然后在用户想要下载时将其读取给用户:


from django.http import HttpResponse
import geopandas as gpd
import shapely
import os
from zipfile import ZipFile
def download_shp_zip(request):
    # just some random polygon
    geometry = shapely.geometry.MultiPolygon([
        shapely.geometry.Polygon([ (0, 0), (0, 1), (1, 1), (1, 0) ]),
        shapely.geometry.Polygon([ (2, 2), (2, 3), (3, 3), (3, 2) ]),
    ])
    # create GeoDataFrame
    gdf = gpd.GeoDataFrame(data={'geometry':geometry}, crs='epsg:4326')
    # some basename to save under
    basename = 'basename'
    #  create folder for this session
    os.mkdir(f"local_folder/{basename}")
    # export gdf to this folder
    gdf.to_file(f"local_folder/{basename}/{basename}.shp")
    # this file now contains many files. just zip them
    zipObj = ZipFile(f"local_folder/{basename}.zip", 'w')
    # zip everything in the folder to the zip
    for file in os.listdir(f"local_folder/{basename}"):
        zipObj.write(f"local_folder/{basename}/{file}")
    # create zip
    zipObj.close()
    # now delete the original files that were zipped
    shutil.rmtree(f"local_folder/{basename}")

    # now we can server the zip file to the user
    filename = f'local_folder/{basename}.zip'
    # check if file exists (just in case)
    try:
        fsock = open(filename, "rb")
    except:
        return HttpResponse(f"File '{basename}' Does Not Exist!",
            content_type='text/plain')
    # create response
    response = HttpResponse(fsock, content_type='application/zip')
    response['Content-Disposition'] = f'attachment; filename={basename}.zip'
    return response
    
Run Code Online (Sandbox Code Playgroud)

此方法有效,但它将文件保存到本地存储,然后提供服务。但是,我想将它们保存到内存中,然后将其提供给用户。我见过人们使用 os.BytesIO 来做类似的事情。我一直在玩,但我无法完全找到任何适合我所寻找的东西。

同样,我有 GeoDataFrame。我想将其转换为 shapefile,然后将其压缩,然后将压缩的文件夹从内存提供给用户,而不将其写入本地存储。

Hel*_*der 5

可以使用tempfile创建文件并在服务后立即删除它。所以这不是您正在寻找的答案,但也许仍然有帮助。根据这个答案

一旦文件处理程序关闭,由 tempfile 创建的任何文件都将被删除。在您的情况下,当您退出 with 语句时。

from django.http import HttpResponse
import geopandas as gpd
import shapely
import os
import tempfile
from zipfile import ZipFile

def download_shp_zip(request):

    # just some random polygon
    geometry = shapely.geometry.MultiPolygon([
        shapely.geometry.Polygon([(0, 0), (0, 1), (1, 1), (1, 0)]),
        shapely.geometry.Polygon([(2, 2), (2, 3), (3, 3), (3, 2)]),
    ])
    # create GeoDataFrame
    gdf = gpd.GeoDataFrame(data={'geometry': geometry}, crs='epsg:4326')

    # some basename to save under
    basename = 'basename'

    # Convert to shapefile and serve it to the user
    with tempfile.TemporaryDirectory() as tmp_dir:

        # Export gdf as shapefile
        gdf.to_file(os.path.join(tmp_dir, f'{basename}.shp'), driver='ESRI Shapefile')

        # Zip the exported files to a single file
        tmp_zip_file_name = f'{basename}.zip'
        tmp_zip_file_path = f"{tmp_dir}/{tmp_zip_file_name}"
        tmp_zip_obj = ZipFile(tmp_zip_file_path, 'w')

        for file in os.listdir(tmp_dir):
            if file != tmp_zip_file_name:
                tmp_zip_obj.write(os.path.join(tmp_dir, file), file)

        tmp_zip_obj.close()

        # Return the file
        with open(tmp_zip_file_path, 'rb') as file:
            response = HttpResponse(file, content_type='application/force-download')
            response['Content-Disposition'] = f'attachment; filename="{tmp_zip_file_name}"'
            return response
Run Code Online (Sandbox Code Playgroud)

另一种选择是在 try-except 块内使用 return 语句,并在 finally 语句中删除文件:

        # Return the zip file as download response and delete it afterwards
        try:
            with open(tmp_zip_file_path, 'rb') as file:
                response = HttpResponse(file, content_type='application/force-download')
                response['Content-Disposition'] = f'attachment; filename="{tmp_zip_file_name}"'
                return response
        finally:
            os.remove(tmp_zip_file_path)
Run Code Online (Sandbox Code Playgroud)

为了获得更好的用户体验,您可以在下载开始之前使用加载 gif。