/* * 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.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(new File(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(((File)fileList.get(i)).getAbsolutePath(), 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((File)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(new File(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(File 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.getAbsolutePath())) { 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.getAbsolutePath())) { throw new ZipException("cannot read folder: " + file.getAbsolutePath()); } String rootFolderPath = null; if (parameters.isIncludeRootFolder()) { if (file.getAbsolutePath() != null) { rootFolderPath = file.getAbsoluteFile().getParentFile() != null ? file.getAbsoluteFile().getParentFile().getAbsolutePath() : ""; } else { rootFolderPath = file.getParentFile() != null ? file.getParentFile().getAbsolutePath() : ""; } } else { rootFolderPath = file.getAbsolutePath(); } 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<br><br> * * <b>Note:</b> 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; } RandomAccessFile outputStream = null; try { for (int i = 0; i < fileList.size(); i++) { File file = (File) fileList.get(i); String fileName = Zip4jUtil.getRelativeFileName(file.getAbsolutePath(), 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 RandomAccessFile prepareFileOutputStream() throws ZipException { String outPath = zipModel.getZipFile(); if (!Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) { throw new ZipException("invalid output path"); } try { File outFile = new File(outPath); if (!outFile.getParentFile().exists()) { outFile.getParentFile().mkdirs(); } return new RandomAccessFile(outFile, InternalZipConstants.WRITE_MODE); } 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( ((File)fileList.get(i)).getAbsolutePath(), parameters.getRootFolderInZip(), parameters.getDefaultFolderPath()); FileHeader fileHeader = Zip4jUtil.getFileHeader(zipModel, relativeFileName); if (fileHeader != null) { totalWork += (Zip4jUtil.getFileLengh(new File(zipModel.getZipFile())) - fileHeader.getCompressedSize()); } } } } } return totalWork; } }