All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.lingala.zip4j.zip.ZipEngine Maven / Gradle / Ivy

/*
* Copyright 2010 Srikanth Reddy Lingala  
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, 
* software distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/

package net.lingala.zip4j.zip;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;

import net.lingala.zip4j.core.NativeFile;
import net.lingala.zip4j.core.NativeStorage;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.io.SplitOutputStream;
import net.lingala.zip4j.io.ZipOutputStream;
import net.lingala.zip4j.model.EndCentralDirRecord;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipModel;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.util.ArchiveMaintainer;
import net.lingala.zip4j.util.CRCUtil;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.Zip4jConstants;
import net.lingala.zip4j.util.Zip4jUtil;

public class ZipEngine {
	
	private ZipModel zipModel;
	
	public ZipEngine(ZipModel zipModel) throws ZipException {
		
		if (zipModel == null) {
			throw new ZipException("zip model is null in ZipEngine constructor");
		}
		
		this.zipModel = zipModel;
	}
	
	public void addFiles(final ArrayList fileList, final ZipParameters parameters,
			final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
		
		if (fileList == null || parameters == null) {
			throw new ZipException("one of the input parameters is null when adding files");
		}
		
		if(fileList.size() <= 0) {
			throw new ZipException("no files to add");
		}
		
		progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_ADD);
		progressMonitor.setState(ProgressMonitor.STATE_BUSY);
		progressMonitor.setResult(ProgressMonitor.RESULT_WORKING);
		
		if (runInThread) {
			progressMonitor.setTotalWork(calculateTotalWork(fileList, parameters));
			progressMonitor.setFileName(((File)fileList.get(0)).getAbsolutePath());
			
			Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
				public void run() {
					try {
						initAddFiles(fileList, parameters, progressMonitor);
					} catch (ZipException e) {
					}
				}
			};
			thread.start();
			
		} else {
			initAddFiles(fileList, parameters, progressMonitor);
		}
	}
	
	private void initAddFiles(ArrayList fileList, ZipParameters parameters,
			ProgressMonitor progressMonitor) throws ZipException {
		
		if (fileList == null || parameters == null) {
			throw new ZipException("one of the input parameters is null when adding files");
		}
		
		if(fileList.size() <= 0) {
			throw new ZipException("no files to add");
		}
		
		if (zipModel.getEndCentralDirRecord() == null) {
			zipModel.setEndCentralDirRecord(createEndOfCentralDirectoryRecord());
		}
		
		ZipOutputStream outputStream  = null;
		InputStream inputStream = null;
		try {
			checkParameters(parameters);
			
			removeFilesIfExists(fileList, parameters, progressMonitor);
			
			boolean isZipFileAlreadExists = Zip4jUtil.checkFileExists(zipModel.getZipFile());
			
			SplitOutputStream splitOutputStream = new SplitOutputStream(zipModel.getZipFile(), zipModel.getSplitLength());
			outputStream = new ZipOutputStream(splitOutputStream, this.zipModel);
			
			if (isZipFileAlreadExists) {
				if (zipModel.getEndCentralDirRecord() == null) {
					throw new ZipException("invalid end of central directory record");
				}
				splitOutputStream.seek(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
			}
			byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
			int readLen = -1;
			for (int i = 0; i < fileList.size(); i++) {
				
				if (progressMonitor.isCancelAllTasks()) {
				    progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
				    progressMonitor.setState(ProgressMonitor.STATE_READY);
				    return;
				}
				
				ZipParameters fileParameters = (ZipParameters) parameters.clone();
				
				progressMonitor.setFileName(((File)fileList.get(i)).getAbsolutePath());
				
				if (!((File)fileList.get(i)).isDirectory()) {
					if (fileParameters.isEncryptFiles() && fileParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
						progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_CALC_CRC);
						fileParameters.setSourceFileCRC((int)CRCUtil.computeFileCRC((NativeStorage)fileList.get(i), progressMonitor));
						progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_ADD);
						
						if (progressMonitor.isCancelAllTasks()) {
						    progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
						    progressMonitor.setState(ProgressMonitor.STATE_READY);
						    return;
						}
					}
					
					if (Zip4jUtil.getFileLengh((File)fileList.get(i)) == 0) {
						fileParameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
					}
				}
				
				outputStream.putNextEntry((NativeStorage)fileList.get(i), fileParameters);
				if (((File)fileList.get(i)).isDirectory()) {
					outputStream.closeEntry();
					continue;
				}
				
				inputStream = new FileInputStream((File)fileList.get(i));
				
				while ((readLen = inputStream.read(readBuff)) != -1) {
					if (progressMonitor.isCancelAllTasks()) {
					    progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
					    progressMonitor.setState(ProgressMonitor.STATE_READY);
					    return;
					}
					
					outputStream.write(readBuff, 0, readLen);
					progressMonitor.updateWorkCompleted(readLen);
				}
				
				outputStream.closeEntry();
				
				if (inputStream != null) {
					inputStream.close();
				}
			}
			
			outputStream.finish();
			progressMonitor.endProgressMonitorSuccess();
		} catch (ZipException e) {
			progressMonitor.endProgressMonitorError(e);
			throw e;
		} catch (Exception e) {
			progressMonitor.endProgressMonitorError(e);
			throw new ZipException(e);
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
				}
			}
			
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
				}
			}
		}
	}
	
	public void addStreamToZip(InputStream inputStream, ZipParameters parameters) throws ZipException {
		if (inputStream == null || parameters == null) {
			throw new ZipException("one of the input parameters is null, cannot add stream to zip");
		}
		
		ZipOutputStream outputStream  = null;
		
		try {
			checkParameters(parameters);
			
			boolean isZipFileAlreadExists = Zip4jUtil.checkFileExists(zipModel.getZipFile());
			
			SplitOutputStream splitOutputStream = new SplitOutputStream(zipModel.getZipFile(), zipModel.getSplitLength());
			outputStream = new ZipOutputStream(splitOutputStream, this.zipModel);
			
			if (isZipFileAlreadExists) {
				if (zipModel.getEndCentralDirRecord() == null) {
					throw new ZipException("invalid end of central directory record");
				}
				splitOutputStream.seek(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
			}
			
			byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
			int readLen = -1;
			
			outputStream.putNextEntry(null, parameters);
			
			if (!parameters.getFileNameInZip().endsWith("/") && 
					!parameters.getFileNameInZip().endsWith("\\")) {
				while ((readLen = inputStream.read(readBuff)) != -1) {
					outputStream.write(readBuff, 0, readLen);
				}
			}
			
			outputStream.closeEntry();
			outputStream.finish();
			
		} catch (ZipException e) {
			throw e;
		} catch (Exception e) {
			throw new ZipException(e);
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					//ignore
				}
			}
		}
	}
	
	public void addFolderToZip(NativeStorage file, ZipParameters parameters, 
			ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
		if (file == null || parameters == null) {
			throw new ZipException("one of the input parameters is null, cannot add folder to zip");
		}
		
		if (!Zip4jUtil.checkFileExists(file)) {
			throw new ZipException("input folder does not exist");
		}
		
		if (!file.isDirectory()) {
			throw new ZipException("input file is not a folder, user addFileToZip method to add files");
		}
		
		if (!Zip4jUtil.checkFileReadAccess(file)) {
			throw new ZipException("cannot read folder: " + file.getPath());
		}
		
		NativeStorage rootFolderPath = null;
		if (parameters.isIncludeRootFolder()) {
			rootFolderPath = file.getParent() != null ? file.getParent() : null;
		} else {
		    rootFolderPath = file;
		}
		
		parameters.setDefaultFolderPath(rootFolderPath);
		
		ArrayList fileList = Zip4jUtil.getFilesInDirectoryRec(file, parameters.isReadHiddenFiles());
		
		if (parameters.isIncludeRootFolder()) {
			if (fileList == null) {
				fileList = new ArrayList();
			}
			fileList.add(file);
		}
		
		addFiles(fileList, parameters, progressMonitor, runInThread);
	}
	
	
	private void checkParameters(ZipParameters parameters) throws ZipException {
		
		if (parameters == null) {
			throw new ZipException("cannot validate zip parameters");
		}
		
		if ((parameters.getCompressionMethod() != Zip4jConstants.COMP_STORE) && 
				parameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
			throw new ZipException("unsupported compression type");
		}
		
		if (parameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
			if (parameters.getCompressionLevel() < 0 && parameters.getCompressionLevel() > 9) {
				throw new ZipException("invalid compression level. compression level dor deflate should be in the range of 0-9");
			}
		}
		
		if (parameters.isEncryptFiles()) {
			if (parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_STANDARD && 
					parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_AES) {
				throw new ZipException("unsupported encryption method");
			}
			
			if (parameters.getPassword() == null || parameters.getPassword().length <= 0) {
				throw new ZipException("input password is empty or null");
			}
		} else {
			parameters.setAesKeyStrength(-1);
			parameters.setEncryptionMethod(-1);
		}
		
	}
	
	/**
	 * Before adding a file to a zip file, we check if a file already exists in the zip file
	 * with the same fileName (including path, if exists). If yes, then we remove this file
	 * before adding the file

* * Note: Relative path has to be passed as the fileName * * @param zipModel * @param fileName * @throws ZipException */ private void removeFilesIfExists(ArrayList fileList, ZipParameters parameters, ProgressMonitor progressMonitor) throws ZipException { if (zipModel == null || zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null || zipModel.getCentralDirectory().getFileHeaders().size() <= 0) { //For a new zip file, this condition satisfies, so do nothing return; } NativeFile outputStream = null; try { for (int i = 0; i < fileList.size(); i++) { NativeStorage file = (NativeStorage) fileList.get(i); String fileName = Zip4jUtil.getRelativeFileName(file, parameters.getRootFolderInZip(), parameters.getDefaultFolderPath()); FileHeader fileHeader = Zip4jUtil.getFileHeader(zipModel, fileName); if (fileHeader != null) { if (outputStream != null) { outputStream.close(); outputStream = null; } ArchiveMaintainer archiveMaintainer = new ArchiveMaintainer(); progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_REMOVE); HashMap retMap = archiveMaintainer.initRemoveZipFile(zipModel, fileHeader, progressMonitor); if (progressMonitor.isCancelAllTasks()) { progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED); progressMonitor.setState(ProgressMonitor.STATE_READY); return; } progressMonitor .setCurrentOperation(ProgressMonitor.OPERATION_ADD); if (outputStream == null) { outputStream = prepareFileOutputStream(); if (retMap != null) { if (retMap.get(InternalZipConstants.OFFSET_CENTRAL_DIR) != null) { long offsetCentralDir = -1; try { offsetCentralDir = Long .parseLong((String) retMap .get(InternalZipConstants.OFFSET_CENTRAL_DIR)); } catch (NumberFormatException e) { throw new ZipException( "NumberFormatException while parsing offset central directory. " + "Cannot update already existing file header"); } catch (Exception e) { throw new ZipException( "Error while parsing offset central directory. " + "Cannot update already existing file header"); } if (offsetCentralDir >= 0) { outputStream.seek(offsetCentralDir); } } } } } } } catch (IOException e) { throw new ZipException(e); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { //ignore } } } } private NativeFile prepareFileOutputStream() throws ZipException { NativeStorage outPath = zipModel.getZipFile(); if (!Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) { throw new ZipException("invalid output path"); } try { NativeStorage outFile = outPath; if (!outFile.getParent().exists()) { outFile.getParent().mkdirs(); } return outFile.write(); } catch (FileNotFoundException e) { throw new ZipException(e); } } private EndCentralDirRecord createEndOfCentralDirectoryRecord() { EndCentralDirRecord endCentralDirRecord = new EndCentralDirRecord(); endCentralDirRecord.setSignature(InternalZipConstants.ENDSIG); endCentralDirRecord.setNoOfThisDisk(0); endCentralDirRecord.setTotNoOfEntriesInCentralDir(0); endCentralDirRecord.setTotNoOfEntriesInCentralDirOnThisDisk(0); endCentralDirRecord.setOffsetOfStartOfCentralDir(0); return endCentralDirRecord; } private long calculateTotalWork(ArrayList fileList, ZipParameters parameters) throws ZipException { if (fileList == null) { throw new ZipException("file list is null, cannot calculate total work"); } long totalWork = 0; for (int i = 0; i < fileList.size(); i++) { if(fileList.get(i) instanceof File) { if (((File)fileList.get(i)).exists()) { if (parameters.isEncryptFiles() && parameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) { totalWork += (Zip4jUtil.getFileLengh((File)fileList.get(i)) * 2); } else { totalWork += Zip4jUtil.getFileLengh((File)fileList.get(i)); } if (zipModel.getCentralDirectory() != null && zipModel.getCentralDirectory().getFileHeaders() != null && zipModel.getCentralDirectory().getFileHeaders().size() > 0) { String relativeFileName = Zip4jUtil.getRelativeFileName( (NativeStorage)fileList.get(i), parameters.getRootFolderInZip(), parameters.getDefaultFolderPath()); FileHeader fileHeader = Zip4jUtil.getFileHeader(zipModel, relativeFileName); if (fileHeader != null) { totalWork += (Zip4jUtil.getFileLengh(zipModel.getZipFile()) - fileHeader.getCompressedSize()); } } } } } return totalWork; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy