Compressing a folder in Javascript while maintaining its original structure

Looking for a way to zip a selected folder upon upload using JavaScript on the client side. When users select a folder through the upload button, I want to maintain the same folder structure and zip it before uploading it to the backend server.


After doing some research: I came across a method that creates a Zip archive, but it compresses all files within the folder without preserving the folder structure.

What I need is to zip the folder while keeping its original structure intact.

Below are the code snippet and library references I used:

JSFiddle link: JSFidler

JSZip official website:

JSZip npm package: https://www.npmjs.com/package/jszip

The JavaScript function looks like this:

          <-- HTML -->
          <input id="uploadID" type="file" accept=".zip" webkitdirectory> 

          //JavaScript
          uploadfolder(e){
            var zip = new JSZip();
            var fileslist = event.target.files;
            var files = zip.folder();
            for(let i=0; i<fileslist.length; i++){
                console.log(fileslist[i].name);
                files.file(fileslist[i].name, fileslist[i].raw, {base64: true});
            }
            zip.generateAsync({type:"blob"}).then(function(content) {
                saveAs(content, "example.zip");
            });
           },

Answer №1

Take a look at the following website links:

  https://gildas-lormeau.github.io/zip.js/
  https://www.npmjs.com/package/zip-folder
  https://stuk.github.io/jszip/

Answer №2

After an extensive search across the vast expanse of the Internet, I found myself unable to locate a satisfactory solution to this particular query. The closest match I stumbled upon was this link, although it did not quite meet my requirements. Determined to find a resolution, I took matters into my own hands and developed the following solution:

const recursivelyZipFolders = (
folder: JSZip,
pathArray: string[],
currentIndex: number,
file: File
) => {
const subFolder = folder.folder(pathArray[currentIndex])!;
currentIndex += 1;
if (currentIndex < pathArray.length - 1) {
    recursivelyZipFolders(subFolder, pathArray, currentIndex, file);
} else {
    subFolder.file(pathArray[pathArray.length - 1], file);
}
};

export const getFilesAsZip = async (files: File[]) => {
try {
    const zip = new JSZip();
    files.forEach((file: any) => {
        // make sure it works for both drag && drop, as well as click & upload 
        const pathArray = file.path
            ? file.path.split("/")
            : file.webkitRelativePath.split("/");
        if (file.path) {
            pathArray.shift();
        }
        if (pathArray.length === 2) {
            zip.file(pathArray[1], file);
        } else if (pathArray.length > 2) {
            const folder = zip.folder(pathArray[1])!;
            if (pathArray.length === 3) {
                folder.file(pathArray[2], file);
            } else {
                recursivelyZipFolders(folder, pathArray, 2, file);
            }
        }
    });
    // zip object now maintains the original folder structure
    return await zip.generateAsync({
        type: "blob",
        compression: "DEFLATE",
        compressionOptions: {
            level: 9,
        },
    });
} catch (error) {
    throw error;
}
};

Testing revealed that this solution successfully handled folders with up to five subfolders. Any suggestions for further enhancements and optimizations are welcome.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to structure this React state container for modularity?

At my workplace, we have developed a state container hook for our React application and related packages. Before discussing what I'd like to achieve with this hook, let me provide some background information. Here is the functional code that's co ...

Creating collections in a Hashtable style using JavaScript

Creating a collection in JavaScript can be done in the following way: Start by initializing an empty collection with var c = {}; Next, you can add items to it. After addition, it will look like: { 'buttonSubmit': function() { /* do some work * ...

Determining when ng-repeat has completed in Angular JS

Is there a way to determine when ng-repeat has completed populating the values in the markup? Since I have numerous values, it may take some time for the rendering process. NG <ul > <li data-ng-repeat="item in values"> ...

Mobile-friendly persistent navigation located beneath the header, inspired by the iOS7 browser design

This issue is really causing me a headache and slowing down progress on my project because I can't seem to figure it out. Currently, I am working on designing a mobile-friendly website and conducting tests in iOS7/OSX. The layout I am aiming for inc ...

Exploring Object Array values with Javascript

I am working with an Object array and I need to implement a contains filter for every property. This means that the search should look for a specific keyword in any of the properties and return the object if it finds a match. Can you please provide guidanc ...

What is the simplest method to package a vue.js frontend into an electron application?

I am working on a vue.js application that connects to an API and can run on different servers. Currently, it is hosted on a web server but I want to provide clients with the option to use it as a desktop application that still communicates with the same AP ...

Switch color in Material-UI based on props

Utilizing code inspired by the Material-UI documentation on customizing the switch, you can customize the switch color to be blue: import React from 'react' import Switch from '@material-ui/core/Switch' import {withStyles} from '@ ...

What is the best way to extract multiple values from a JavaScript variable and transfer them to Node.js?

Script JavaScript script snippet embedded at the bottom of an HTML file: var savedValues = [] var currentId = document.getElementById("fridgeFreezer").value function handleChange() { // Logic to handle user input changes: var temp = document.ge ...

The chai expect statement is causing an assertion error that I am currently encountering

Exploring the combination of different data types using a simple addition function. For instance, when adding 1 + 1, we expect to get 2, and when adding 1 + "one", the result should be "1one". Below is the content of my functions.js file: module.exports = ...

Assess the HTML containing v-html injection

Is there a way to inject raw HTML using Vue, especially when the HTML contains Vue markup that needs to be evaluated? Consider the following example where HTML is rendered from a variable: <p v-html="markup"></p> { computed: { m ...

Having trouble running classes using Maven test with the Testng.xml file in the terminal, however, it runs smoothly in Eclipse

While I have been successful in running my solution through the testng suit in the Eclipse console, I am facing difficulties executing the testng.xml file via Maven integrated with Sauce Labs in the terminal. Output received on the terminal: ------------ ...

"Here's a cool trick: A guide on dynamically inserting a value into {% url tag %} using JavaScript within

Incorporating leaflet JS into a Django template, the aim is to display markers on a map where latitude and longitude are sourced from a queryset. Sending the queryset through views and utilizing the Django template language within <script> tags seeme ...

The functionality of images and links is compromised when they are assigned as values to properties within a JSON object

My images and links are not working, even after declaring them globally. When I write the src directly into src, everything seems fine but the alert pops up with the same URL and img src. Can anyone help? var combo0; var combo1; var combo2; var combo3; ...

Strategies for detecting when a child checkbox is clicked within a parent li element

Below is a snippet of Vue Code illustrating the structure we're working with: <template> <li class="mm-product-item" v-on:click="edit(item)"> <p class="mm-product-info input-field"> <input ...

Error: Attempting to assign a value to a property of #<Object> that is read-only

I'm working on a task management application and encountering an issue when trying to assign an array of tasks stored in localStorage to an array named todayTasks. The error message being thrown is causing some disruption. https://i.sstatic.net/uFKWR. ...

Innovative ways to design a responsive carousel with Bootstrap

My goal was to create a slider with divisions inside a bootsrap carousel, and here is what I did. However, I am looking for guidance on how to adjust it so that on screens smaller than sm (of bootstrap), there are only two divisions in one carousel, and a ...

Implementing pagination in Webgrid using AJAX post method

I've developed this JavaScript code: function PartialViewLoad() { $.ajaxSetup({ cache: false }); $.ajax({ url: "/ControllerAlpha/MethodBeta", type: "GET", dataType: "html", data: { s ...

Implications of using literals, objects, or classes as arguments in functions that are called multiple times can have

Context A project I'm working on involves a scenario where a Shape class is triggering a function call SetPosition( x, y ) on a Drawer class. As part of this process, the Drawer class needs to retain the values (x, y) passed through SetPosition. The ...

Express JS causing NodeJS error | "Issue with setting headers: Unable to set headers after they have been sent to the client"

As I embark on my journey to learn the fundamentals of API development, I am following a tutorial on YouTube by Ania Kubow. The tutorial utilizes three JavaScript libraries: ExpressJS, Cheerio, and Axios. While I have been able to grasp the concepts being ...

Unlocking the Power of Rendering DOM Elements with Protractor

Recently, I began learning protractor and encountered some difficulties extracting text from a DOM object. Here is a snippet of code from an AngularJS application: "<div class="input-group application-item-field"> <input type="te ...