天天看點

Android實作檔案壓縮與解壓縮

    此篇部落客要是記錄如何對檔案夾進行壓縮以及解壓縮操作,結合網上多位大俠的文章,針對自己的情況改動了下,廢話不多說

直接上代碼:(壓縮與解壓縮有兩種不同的實作方法,選其一即可)

首先是壓縮操作:

/**
	 * [對指定路徑下檔案的壓縮處理]
	 * [功能較長的描述]
	 * @param src 徑位址
	 * @param archive 指定到壓縮檔案夾的路徑
	 * @param comment 描述
	 * @throws FileNotFoundException 檔案沒有找到異常
	 * @throws IOException  IO輸入異常
	 */
	public void writeByApacheZipOutputStream(String[] src, String archive,
			String comment) throws FileNotFoundException, IOException {
		Log.e(TAG, "writeByApacheZipOutputStream");
		// ----壓縮檔案:
		FileOutputStream f = new FileOutputStream(archive);
		// 使用指定校驗和建立輸出流
		CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32());
		ZipOutputStream zos = new ZipOutputStream(csum);
		// 支援中文
		zos.setEncoding("GBK");
		BufferedOutputStream out = new BufferedOutputStream(zos);
		// 設定壓縮包注釋
		zos.setComment(comment);
		// 啟用壓縮
		zos.setMethod(ZipOutputStream.DEFLATED);
		// 壓縮級别為最強壓縮,但時間要花得多一點
		zos.setLevel(Deflater.BEST_COMPRESSION);
		// 如果為單個檔案的壓縮在這裡修改
		for (int i = 0; i < src.length; i++) {
			File srcFile = new File(src[i]);
			if (!srcFile.exists()
					|| (srcFile.isDirectory() && srcFile.list().length == 0)) {
				Log.e(TAG, "!srcFile.exists()");
				throw new FileNotFoundException(
						"File must exist and ZIP file must have at least one entry.");
			}
			String strSrcString = src[i];
			// 擷取壓縮源所在父目錄
			strSrcString = strSrcString.replaceAll("", "/");
			String prefixDir = null;
			if (srcFile.isFile()) {
				prefixDir = strSrcString.substring(0,
						strSrcString.lastIndexOf("/") + 1);
			} else {
				prefixDir = (strSrcString.replaceAll("/$", "") + "/");
			}
			// 如果不是根目錄
			if (prefixDir.indexOf("/") != (prefixDir.length() - 1)
					&& isCreateSrcDir) {
				prefixDir = prefixDir.replaceAll("[^/]+/$", "");
			}
			// 開始壓縮
			writeRecursive(zos, out, srcFile, prefixDir);
		}
		out.close();
		// 注:校驗和要在流關閉後才準備,一定要放在流被關閉後使用
		Log.e(TAG, "Checksum: " + csum.getChecksum().getValue());
		@SuppressWarnings("unused")
		BufferedInputStream bi;
	}

     /**
	 * [遞歸壓縮 使用 org.apache.tools.zip.ZipOutputStream 類進行壓縮,它的好處就是支援中文路徑,
	 * 而Java類庫中的 java.util.zip.ZipOutputStream 壓縮中文檔案名時壓縮包會出現亂碼。 使用 apache
	 * 中的這個類與 java 類庫中的用法是一新的,隻是能設定編碼方式了。]<BR>
	 * [功能較長的描述]
	 * @param zos
	 * @param bo
	 * @param srcFile
	 * @param prefixDir
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static void writeRecursive(ZipOutputStream zos,
			BufferedOutputStream bo, File srcFile, String prefixDir)
			throws IOException, FileNotFoundException {
		Log.e(TAG, "writeRecursive");
		ZipEntry zipEntry;
		String filePath = srcFile.getAbsolutePath().replaceAll("", "/")
				.replaceAll("//", "/");
		if (srcFile.isDirectory()) {
			filePath = filePath.replaceAll("/$", "") + "/";
		}
		String entryName = filePath.replace(prefixDir, "").replaceAll("/$", "");
		if (srcFile.isDirectory()) {
			if (!"".equals(entryName)) {
				Log.e(TAG, "正在建立目錄 - " + srcFile.getAbsolutePath()
						+ " entryName=" + entryName);
				// 如果是目錄,則需要在寫目錄後面加上 /
				zipEntry = new ZipEntry(entryName + "/");
				zos.putNextEntry(zipEntry);
			}
			File srcFiles[] = srcFile.listFiles();
			for (int i = 0; i < srcFiles.length; i++) {
				if (srcFiles[i].isDirectory()
						&&  可選指定子目錄篩選) {
					continue;
				}
				writeRecursive(zos, bo, srcFiles[i], prefixDir);
			}
		} else {
			Log.e(TAG, "正在寫檔案 - " + srcFile.getAbsolutePath() + " entryName="
					+ entryName);
			BufferedInputStream bi = new BufferedInputStream(
					new FileInputStream(srcFile));
			// 開始寫入新的ZIP檔案條目并将流定位到條目資料的開始處
			zipEntry = new ZipEntry(entryName);
			zos.putNextEntry(zipEntry);
			byte[] buffer = new byte[1024];
			int readCount = bi.read(buffer);
			while (readCount != -1) {
				bo.write(buffer, 0, readCount);
				readCount = bi.read(buffer);
			}
			// 注,在使用緩沖流寫壓縮檔案時,一個條件完後一定要重新整理一把,不
			// 然可能有的内容就會存入到後面條目中去了
			bo.flush();
			// 檔案讀完後關閉
			bi.close();
		}
	}
           

 接下來自然是解壓縮操作咯:

/**
	 * [* 使用 org.apache.tools.zip.ZipFile 解壓檔案,它與 java 類庫中的
	 * java.util.zip.ZipFile 使用方式是一新的,隻不過多了設定編碼方式的 接口。
	 * 注,apache 沒有提供 ZipInputStream 類,是以隻能使用它提供的ZipFile 來讀取壓縮檔案。]<BR>
	 * @param archive 壓縮包路徑
	 * @param decompressDir 解壓路徑
	 * @throws IOException
	 * @throws FileNotFoundException
	 * @throws ZipException
	 */
	public static void readByApacheZipFile(String archive, String decompressDir)
			throws IOException, FileNotFoundException, ZipException {
		Log.e(TAG, "readByApacheZipFile");
		BufferedInputStream bi;
		ZipFile zf = new ZipFile(archive, "GBK");// 支援中文
		Enumeration e = zf.getEntries();
		while (e.hasMoreElements()) {
			ZipEntry ze2 = (ZipEntry) e.nextElement();
			String entryName = ze2.getName();
			Log.e(TAG, entryName);
			String path = decompressDir + "/" + entryName;
			if (ze2.isDirectory()) {
				Log.e(TAG, "正在建立解壓目錄 - " + entryName);
				File decompressDirFile = new File(path);
				if (!decompressDirFile.exists()) {
					decompressDirFile.mkdirs();
				}
			} else {
				Log.e(TAG, "正在建立解壓檔案 - " + entryName);
				String fileDir = path.substring(0, path.lastIndexOf("/"));
				File fileDirFile = new File(fileDir);
				if (!fileDirFile.exists()) {
					fileDirFile.mkdirs();
				}
				BufferedOutputStream bos = new BufferedOutputStream(
						new FileOutputStream(decompressDir + "/" + entryName));
				bi = new BufferedInputStream(zf.getInputStream(ze2));
				byte[] readContent = new byte[1024];
				int readCount = bi.read(readContent);
				while (readCount != -1) {
					bos.write(readContent, 0, readCount);
					readCount = bi.read(readContent);
				}
				bos.close();
			}
		}
		zf.close();
	}

	/**
	 * 
	 * [使用 java api 中的 ZipInputStream 類解壓檔案,但如果壓縮時采用了
	 * org.apache.tools.zip.ZipOutputStream時,而不是 java 類庫中的
	 * java.util.zip.ZipOutputStream時,該方法不能使用,原因就是編碼方 式不一緻導緻,運作時會抛如下異常:
	 * java.lang.IllegalArgumentException at
	 * java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:290)
	 * 
	 * 當然,如果壓縮包使用的是java類庫的java.util.zip.ZipOutputStream 壓縮而成是不會有問題的,但它不支援中文 ]<BR>
	 * [功能較長的描述]
	 * @param archive
	 *            壓縮包路徑
	 * @param decompressDir
	 *            解壓路徑
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws InterruptedException 
	 */
	public static void readByZipInputStream(String archive, String decompressDir)
			throws FileNotFoundException, IOException{
		BufferedInputStream bi;
		// ----解壓檔案(ZIP檔案的解壓縮實質上就是從輸入流中讀取資料):
		Log.e(TAG, "開始讀壓縮檔案");
		FileInputStream fi = new FileInputStream(archive);
		CheckedInputStream csumi = new CheckedInputStream(fi, new CRC32());
		ZipInputStream in2 = new ZipInputStream(csumi);
		bi = new BufferedInputStream(in2);
		java.util.zip.ZipEntry ze;// 壓縮檔案條目
		// 周遊壓縮包中的檔案條目
		while ((ze = in2.getNextEntry()) != null) {
			String entryName = ze.getName();
			Log.e(TAG, "擷取到的檔案名稱 - " + entryName);
			if (ze.isDirectory()) {
				entryName = entryName.replace("/", "");
				Log.e(TAG, "正在建立解壓目錄 - " + entryName);
				File decompressDirFile = new File(decompressDir + "/"
						+ entryName);
				if (!decompressDirFile.exists()) {
					decompressDirFile.mkdirs();
					ShellUtils.updateChmod(decompressDir + "/" + entryName);
					try {
						Thread.sleep(600);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} else {
				String fileName = decompressDir + "/" + entryName;
				Log.e(TAG, "正在建立解壓檔案 - " + fileName);
				BufferedOutputStream bos = new BufferedOutputStream(
						new FileOutputStream(fileName));
				byte[] buffer = new byte[1024 * 5];
				int readCount = bi.read(buffer);
				while (readCount != -1) {
					bos.write(buffer, 0, readCount);
					readCount = bi.read(buffer);
				}
				bos.close();
				ShellUtils.updateChmod(fileName);
				ExtraHelpUtils.threadSleep(200);
			}
		}
		bi.close();
	}
           

 好了,至于調用方法,此處就不多做說明了。。很簡單的