Comprehensive functionality for modifying existing ZIP archives including updating file contents, deleting entries, and managing archive structure without full recreation.
Modify the content of existing entries within the archive while preserving metadata and structure.
/**
* Update existing entry content
* @param {ZipEntry|string} entry - Entry to update (ZipEntry object or entry name)
* @param {Buffer} content - New content as Buffer
*/
updateFile(entry, content);Usage Examples:
const AdmZip = require("adm-zip");
const zip = new AdmZip("./existing-archive.zip");
// Update file by name
const newContent = Buffer.from("Updated content", "utf8");
zip.updateFile("config.txt", newContent);
// Update JSON configuration
const config = { version: "2.0", debug: false };
const jsonContent = Buffer.from(JSON.stringify(config, null, 2), "utf8");
zip.updateFile("settings/config.json", jsonContent);
// Update file using ZipEntry object
const entry = zip.getEntry("data/users.csv");
if (entry) {
const csvData = "name,email\nJohn,john@example.com\nJane,jane@example.com";
zip.updateFile(entry, Buffer.from(csvData, "utf8"));
}
// Update binary file
const fs = require("fs");
const imageBuffer = fs.readFileSync("./new-logo.png");
zip.updateFile("assets/logo.png", imageBuffer);
// Update multiple files
const updates = [
{ name: "version.txt", content: "v1.2.3" },
{ name: "changelog.md", content: "## Version 1.2.3\n- Bug fixes" },
{ name: "package.json", content: JSON.stringify({ version: "1.2.3" }) }
];
updates.forEach(update => {
zip.updateFile(update.name, Buffer.from(update.content, "utf8"));
});
// Save changes
zip.writeZip("./updated-archive.zip");Remove entries from the archive with options for handling subdirectories and nested content.
/**
* Delete entry and optionally its subfolders
* @param {ZipEntry|string} entry - Entry to delete (ZipEntry object or entry name)
* @param {boolean} [withsubfolders] - Delete subfolders too (default: true)
*/
deleteFile(entry, withsubfolders);
/**
* Delete single entry without affecting nested entries
* @param {ZipEntry|string} entry - Entry to delete (ZipEntry object or entry name)
*/
deleteEntry(entry);Usage Examples:
const zip = new AdmZip("./archive.zip");
// Delete single file
zip.deleteFile("temp-file.txt");
// Delete directory and all its contents
zip.deleteFile("temp-folder/", true);
// Delete directory but keep its contents as orphaned entries
zip.deleteFile("old-structure/", false);
// Delete using ZipEntry object
const entry = zip.getEntry("obsolete/old-data.json");
if (entry) {
zip.deleteFile(entry);
}
// Delete only the directory entry, not its files
zip.deleteEntry("empty-folder/");
// Conditional deletion based on file patterns
const entries = zip.getEntries();
entries.forEach(entry => {
// Delete temporary files
if (entry.entryName.includes('.tmp') || entry.entryName.includes('~')) {
zip.deleteFile(entry);
}
// Delete old log files
if (entry.entryName.startsWith('logs/') && entry.entryName.endsWith('.log')) {
const fileDate = new Date(entry.header.time);
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
if (fileDate < thirtyDaysAgo) {
zip.deleteFile(entry);
}
}
});
// Bulk deletion with confirmation
function deleteWithConfirmation(entryNames) {
const existingEntries = entryNames.filter(name => zip.getEntry(name) !== null);
if (existingEntries.length === 0) {
console.log("No entries found to delete");
return;
}
console.log(`Deleting ${existingEntries.length} entries:`);
existingEntries.forEach(name => console.log(` - ${name}`));
existingEntries.forEach(name => {
zip.deleteFile(name);
});
console.log("Deletion completed");
}
deleteWithConfirmation(['old-config.ini', 'backup/', 'temp-data.csv']);Reorganize archive contents by combining update and delete operations for complex structural changes.
Usage Examples:
const zip = new AdmZip("./project.zip");
// Move files to new directory structure
function restructureArchive() {
const entries = zip.getEntries();
const moves = [];
// Identify files to move
entries.forEach(entry => {
if (entry.entryName.startsWith('old-src/') && !entry.isDirectory) {
const newPath = entry.entryName.replace('old-src/', 'src/');
moves.push({
oldEntry: entry,
newPath: newPath,
content: entry.getData()
});
}
});
// Perform moves (add new, delete old)
moves.forEach(move => {
// Add file to new location
zip.addFile(move.newPath, move.content, move.oldEntry.comment, move.oldEntry.attr);
// Delete from old location
zip.deleteFile(move.oldEntry);
});
console.log(`Moved ${moves.length} files from old-src/ to src/`);
}
restructureArchive();
// Flatten directory structure
function flattenDirectory(sourceDir, targetDir) {
const entries = zip.getEntries();
entries.forEach(entry => {
if (entry.entryName.startsWith(sourceDir) && !entry.isDirectory) {
const fileName = entry.entryName.split('/').pop();
const newPath = targetDir + fileName;
// Add flattened file
zip.addFile(newPath, entry.getData(), entry.comment, entry.attr);
// Remove original
zip.deleteFile(entry);
}
});
// Remove empty source directory
zip.deleteFile(sourceDir);
}
flattenDirectory('nested/deep/structure/', 'flat/');
// Rename files with pattern
function renameFiles(pattern, replacement) {
const entries = zip.getEntries();
entries.forEach(entry => {
if (!entry.isDirectory && pattern.test(entry.entryName)) {
const newName = entry.entryName.replace(pattern, replacement);
// Add with new name
zip.addFile(newName, entry.getData(), entry.comment, entry.attr);
// Remove old entry
zip.deleteFile(entry);
console.log(`Renamed: ${entry.entryName} -> ${newName}`);
}
});
}
// Rename all .txt files to .text
renameFiles(/\.txt$/, '.text');Combine multiple archives or split large archives into smaller parts.
Usage Examples:
// Merge multiple archives
function mergeArchives(sourceArchives, outputPath) {
const mergedZip = new AdmZip();
sourceArchives.forEach((archivePath, index) => {
const sourceZip = new AdmZip(archivePath);
const entries = sourceZip.getEntries();
entries.forEach(entry => {
if (!entry.isDirectory) {
// Add prefix to avoid conflicts
const prefixedName = `archive${index + 1}/${entry.entryName}`;
mergedZip.addFile(prefixedName, entry.getData(), entry.comment, entry.attr);
}
});
});
mergedZip.writeZip(outputPath);
console.log(`Merged ${sourceArchives.length} archives into ${outputPath}`);
}
mergeArchives(['./part1.zip', './part2.zip', './part3.zip'], './merged.zip');
// Split archive by size
function splitArchiveBySize(sourceArchive, maxSizeBytes, outputPrefix) {
const sourceZip = new AdmZip(sourceArchive);
const entries = sourceZip.getEntries();
let currentZip = new AdmZip();
let currentSize = 0;
let partNumber = 1;
entries.forEach(entry => {
if (!entry.isDirectory) {
const entrySize = entry.header.compressedSize;
// If adding this entry would exceed limit, start new archive
if (currentSize + entrySize > maxSizeBytes && currentSize > 0) {
currentZip.writeZip(`${outputPrefix}_part${partNumber}.zip`);
console.log(`Created part ${partNumber} (${currentSize} bytes)`);
currentZip = new AdmZip();
currentSize = 0;
partNumber++;
}
currentZip.addFile(entry.entryName, entry.getData(), entry.comment, entry.attr);
currentSize += entrySize;
}
});
// Save final part
if (currentSize > 0) {
currentZip.writeZip(`${outputPrefix}_part${partNumber}.zip`);
console.log(`Created part ${partNumber} (${currentSize} bytes)`);
}
console.log(`Split into ${partNumber} parts`);
}
// Split into 10MB parts
splitArchiveBySize('./large-archive.zip', 10 * 1024 * 1024, './split');
// Split archive by file type
function splitArchiveByType(sourceArchive, outputDir) {
const sourceZip = new AdmZip(sourceArchive);
const entries = sourceZip.getEntries();
const typeGroups = {};
// Group entries by file extension
entries.forEach(entry => {
if (!entry.isDirectory) {
const ext = entry.entryName.split('.').pop().toLowerCase() || 'no-extension';
if (!typeGroups[ext]) {
typeGroups[ext] = [];
}
typeGroups[ext].push(entry);
}
});
// Create separate archives for each type
Object.keys(typeGroups).forEach(ext => {
const typeZip = new AdmZip();
typeGroups[ext].forEach(entry => {
typeZip.addFile(entry.entryName, entry.getData(), entry.comment, entry.attr);
});
const outputPath = `${outputDir}/${ext}-files.zip`;
typeZip.writeZip(outputPath);
console.log(`Created ${outputPath} with ${typeGroups[ext].length} files`);
});
}
splitArchiveByType('./mixed-content.zip', './split-by-type');Analyze archive contents and perform cleanup operations based on various criteria.
Usage Examples:
const zip = new AdmZip("./archive.zip");
// Remove duplicate files based on content
function removeDuplicateFiles() {
const entries = zip.getEntries();
const contentMap = new Map();
const duplicates = [];
// Build content hash map
entries.forEach(entry => {
if (!entry.isDirectory) {
const content = entry.getData();
const contentHash = require('crypto').createHash('md5').update(content).digest('hex');
if (contentMap.has(contentHash)) {
// Found duplicate
duplicates.push({
original: contentMap.get(contentHash),
duplicate: entry.entryName
});
} else {
contentMap.set(contentHash, entry.entryName);
}
}
});
// Remove duplicates
duplicates.forEach(dup => {
console.log(`Removing duplicate: ${dup.duplicate} (same as ${dup.original})`);
zip.deleteFile(dup.duplicate);
});
console.log(`Removed ${duplicates.length} duplicate files`);
}
removeDuplicateFiles();
// Clean up empty directories
function removeEmptyDirectories() {
const entries = zip.getEntries();
const directories = entries.filter(entry => entry.isDirectory);
let removedCount = 0;
directories.forEach(dir => {
const hasChildren = entries.some(entry =>
entry.entryName.startsWith(dir.entryName) &&
entry.entryName !== dir.entryName
);
if (!hasChildren) {
console.log(`Removing empty directory: ${dir.entryName}`);
zip.deleteEntry(dir.entryName);
removedCount++;
}
});
console.log(`Removed ${removedCount} empty directories`);
}
removeEmptyDirectories();
// Archive size optimization
function optimizeArchive() {
const entries = zip.getEntries();
let originalSize = 0;
let optimizedSize = 0;
entries.forEach(entry => {
if (!entry.isDirectory) {
originalSize += entry.header.size;
// Re-compress with better settings if needed
const content = entry.getData();
if (content && content.length > 0) {
zip.updateFile(entry, content); // This will re-compress
optimizedSize += content.length;
}
}
});
console.log(`Archive optimization: ${originalSize} -> ${optimizedSize} bytes`);
}
optimizeArchive();