Android NDKでZIP書庫内の画像ファイルを解凍する

test122_01.png
今回はAndroid NDKにより、ZIP書庫内に含まれる画像ファイルを解凍するためのクラスを作成する。ZIP書庫の操作には「Android NDKの標準ライブラリにlibunzipを追加して利用する」で用意したlibunzipを利用する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。C++、HelloJNIでSTLを有効にした。


test122_02.png
自動生成されたプロジェクトをEclipseに取り込み、「ZipedImages.h」ファイルを新規作成してZIP書庫操作用のクラスを実装する。

指定されたZIP書庫を開き、書庫内にあるすべてのファイル名をチェックして拡張子が画像ならstd::vectorにファイル位置を保存。そしてそのstd::vector上のインデックス指定によりファイルを指定フォルダへと解凍するという流れにした。かなり中途半端かつ汎用性のない実装になっている。汎用的に実装しようと思うと、ファイル名に日本語が使われていた場合やら何やら色々考えないといけないのでやめました。
#ifndef	_ZIPEDIMAGES_H
#define	_ZIPEDIMAGES_H

#include<string>
#include<vector>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <android/log.h>


#include "unzip.h"

#define	MAX_PATH	256

#define	bool	unsigned int
#define	true	(1)
#define	false	(0)




class	CZipedImages
{
protected:

	unzFile		_zipFile;
	std::vector<unz_file_pos>	_vFilePos;


public:

	CZipedImages()
	{
		_zipFile = 0;
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::CZipedImages()");
	}

	~CZipedImages()
	{
		Close();
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::~CZipedImages()");
	}

	bool	IsOpen(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::IsOpen()");
		return	(_zipFile != 0) ? true : false;
	}


	bool	OpenZipFile(const char* pszFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::OpenZipFile()");
		if(IsOpen() == true)
			return	false;

		int		nRet;

		_zipFile = unzOpen(pszFile);

		nRet = unzGoToFirstFile(_zipFile);
		while(nRet == UNZ_OK)
		{
			char	pszFileName[MAX_PATH];
			unz_file_info	info;
			unz_file_pos	pos;
			std::string		strFile;
			std::string		strExt;

			unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
			strFile	= pszFileName;
			strExt	= strFile.substr(strFile.length() - 4,4);		//いい加減な方法で拡張子取得

			if(strExt == ".JPG" || strExt == ".jpg" || strExt == ".PNG" || strExt == ".png")				//いい加減な方法で拡張子チェック
			{
				unzGetFilePos(_zipFile,&pos);
				_vFilePos.push_back(pos);
			}
			else
			{
				//処理されないファイル/フォルダをログ出力
				__android_log_print(ANDROID_LOG_INFO,TAG,"NOT process : (%s)",pszFileName);
			}

			nRet = unzGoToNextFile(_zipFile);
		}

		return	true;
	}


	void	Close(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Close()");

		if(_zipFile != 0)
			unzClose(_zipFile);
		_zipFile = 0;
		_vFilePos.clear();
	}



	int		GetCount(void)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::GetCount()");

		return	_vFilePos.size();
	}


	bool	Extract(int nIndex,const char* pszFolder,std::string* pstrFile)
	{
		__android_log_write(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract()");

		if(IsOpen() == false)
			return	false;

		if(nIndex > _vFilePos.size() || nIndex < 0)
			return	false;

		int		nRet;
		char	pszFileName[MAX_PATH];
		unz_file_info	info;

		nRet = unzGoToFilePos(_zipFile,&_vFilePos[nIndex]);
		if(nRet != UNZ_OK)
			return	false;

		nRet = unzGetCurrentFileInfo(_zipFile,&info,pszFileName,MAX_PATH,NULL,0,NULL,0);
		if(nRet != UNZ_OK)
			return	false;
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() in process %s",pszFileName);


		std::string	strPath;

		//保存先ファイルパス作成
		{
			strPath = pszFolder;
			if(strPath.substr(strPath.length() - 1,1) != "/")
				strPath += "/";

			if(pstrFile != NULL && *pstrFile != "")
			{
				if((*pstrFile)[0] != '/')
					strPath += *pstrFile;
				else
					strPath += pstrFile->substr(1,pstrFile->length() - 1);
			}
			else
			{
				char	pszBuff[256];
				std::string	strExt;

				strExt	= pszFileName;
				strExt	= strExt.substr(strExt.length() - 4,4).c_str();	//いい加減な方法で拡張子取得
				sprintf(pszBuff,"%d%s",nIndex,strExt.c_str());

				if(pstrFile)
					*pstrFile = pszBuff;
				strPath.append(pszBuff);
			}
		}
		__android_log_print(ANDROID_LOG_DEBUG,TAG,"CZipedImages::Extract() : save to %s",strPath.c_str());

		//解凍
		{
			FILE	*fp;

			fp = fopen(strPath.c_str(),"w");
			if(fp == NULL)
				return	false;

			char	szBuffer[32768];
			int		dwSizeRead;

			nRet = unzOpenCurrentFile(_zipFile);
			if(nRet == UNZ_OK)
			{
				while ((dwSizeRead = unzReadCurrentFile(_zipFile,szBuffer,sizeof(szBuffer))) > 0)
				{
					fwrite(szBuffer,1,dwSizeRead,fp);
				}
				unzCloseCurrentFile(_zipFile);
			}
			fclose(fp);
		}

		return	true;
	}
};

#endif

test122_03.png そしてメイン処理内でCZipedImaesクラスのテスト用に2つのファイルを解凍する処理を追加する。
#if(true)
#define LOCAL_LOG
#define LOCAL_LOGD
#endif

#include <string.h>
#include <jni.h>

#ifdef LOCAL_LOG
#include <android/log.h>
#endif


#define	TAG	"Test122"
#include "ZipedImages.h"



extern "C"
jstring Java_com_Test122_Test122Act_stringFromJNI(JNIEnv* env,jobject thiz)
{
	CZipedImages	cZip;

	cZip.OpenZipFile("/sdcard/desire_img.zip");

	bool		ret;
	int			nSize;
	std::string	strFile;

	nSize = cZip.GetCount();

	//最初のファイルを解凍
	strFile = "";
	ret = cZip.Extract(0,"/sdcard",&strFile);
	if(ret)
		__android_log_print(ANDROID_LOG_DEBUG,"Test122","save to %s",strFile.c_str());

	//5番目のファイルを解凍
	strFile = "";
	ret = cZip.Extract(5,"/sdcard",&strFile);
	if(ret)
		__android_log_print(ANDROID_LOG_DEBUG,"Test122","save to %s",strFile.c_str());

	cZip.Close();

	return env->NewStringUTF("Test122text");
}

test122_04.png 最後にAndroid.mkにlibunzipとlibzipのライブラリを追加する。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Test122
LOCAL_SRC_FILES := Test122.cpp
LOCAL_LDLIBS += -lz -lunzip

LOCAL_IS_SUPPORT_LOG := true
ifeq ($(LOCAL_IS_SUPPORT_LOG),true)
	LOCAL_LDLIBS += -llog
endif

include $(BUILD_SHARED_LIBRARY)

test122_05.png
これで実行するとSDカード上のZIP書庫を読み込み、その中にあるファイルのうち2つが解凍された。




プロジェクトファイルをダウンロード


カテゴリー「android」 のエントリー