Using AbstractPath to save Shapefile to in memory zip archive

Hi, new user here. Rather to save SHP, SHX etc… to actual files I would like to save them to an in memory zip file using System.IO.Compression.ZipArchive.

If I trace what is called it seems that WithLocation and then Open is called with the shape.prj, shape.shx, shape.dbf, shape.cpg location but NOT shape.shp. I don’t see from where shape.shp could be written ?

If someone can give the right direction to property implement this.

Got it, Open is called later on the first instance with the shape.shp location (not with shape.prj as I though which make sense as no prj file is currently created).
Should be able to implement what I need…

Hi, @Pat17

Thank you for your interest in the Aspose.GIS product.

It’s possible, there is one way. In order to make Aspose.GIS work with the zip in memory, an inheritor of Aspose.Gis.AbstractPath must be implemented. This code contains an example of such inheritor and how to use it.

How to save Shapefile to in memory zip archive

        // get streams and associate with names. the extensions of mapped names should be the same as in files.
        Dictionary<string, Stream> storage = new Dictionary<string, Stream>();

        // add writable streams. aspose.gis requires a 'writable.cpg' file too.
        storage.Add("writable.shp", new MemoryStream());
        storage.Add("writable.shx", new MemoryStream());
        storage.Add("writable.dbf", new MemoryStream());
        storage.Add("writable.prj", new MemoryStream());
        storage.Add("writable.cpg", new MemoryStream());

        // write to memory
        using (var writePath = new MultiStreamPath("writable.shp", storage))
        using (var writeLayer = Drivers.Shapefile.CreateLayer(writePath))
        {
            // copy columns info
            writeLayer.Attributes.Add(new FeatureAttribute("Title", AttributeDataType.String));

            // add features
            var feature = writeLayer.ConstructFeature();
            feature.Geometry = Geometry.FromText("POINT (30 10)");
            feature.SetValue("Title", "House");
            writeLayer.Add(feature);

        }

        // check sizes
        Console.WriteLine(storage["writable.shp"].Length);
        Console.WriteLine(storage["writable.shx"].Length);
        Console.WriteLine(storage["writable.dbf"].Length);
        Console.WriteLine(storage["writable.cpg"].Length);
        Console.WriteLine(storage["writable.prj"].Length);//may be '0'

        // put to zip
        var zipStream = new MemoryStream();
        ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create);
        foreach(var item in storage)
        {
            var entry = zip.CreateEntry(item.Key);
            using (var entryStream = entry.Open())
            {
                item.Value.Position = 0;
                item.Value.CopyTo(entryStream);
            }
        }

        // dispose in-memory storage
        foreach (var item in storage)
        {
            item.Value.Dispose();
        }

Class allows to read\write in memory for the multi file formats (like shapefile).

public class MultiStreamPath : AbstractPath, IDisposable
{
    private readonly string _entryName;
    private readonly Dictionary<string, Stream> _storage;
    private readonly List<Stream> _streams = new List<Stream>();

    public MultiStreamPath(string entryName, Dictionary<string, Stream> storage)
    {
        this._storage = storage;
        _entryName = entryName;
    }

    public override bool IsFile()
    {
        // In our terms, if there is a entry on our stream list, we return true.
        return this._storage.ContainsKey(this._entryName);
    }

    public override void Delete()
    {
        this._storage.Remove(this._entryName);
    }

    public override Stream Open(FileAccess access)
    {
        var stream = this._storage[this._entryName];
        var openedStream = AbstractPath.FromStream(stream).Open(access);
        this._streams.Add(openedStream);
        return openedStream;
    }

    public override IEnumerable<AbstractPath> ListDirectory()
    {
        var list = new List<AbstractPath>();
        foreach (var item in this._storage)
        {
            list.Add(new MultiStreamPath(item.Key, this._storage));
        }

        return list;
    }

    public void Dispose()
    {
        foreach (var stream in this._streams)
        {
            stream?.Dispose();
        }
    }

    protected override AbstractPath WithLocation(string newLocation)
    {
        return new MultiStreamPath(newLocation, this._storage);
    }

    public override string Location => this._entryName;
    public override char Separator => '/';
}

Please feel free to contact us if you have any other issues or difficulty using the product.