zip文件解壓縮

class CountingInputStream extends BufferedInputStream {

    private long bytesReadMark = 0;  //用於存儲文件位置標識
    private long bytesRead = 0;         //當前讀取文件字節數
    //構造一個BufferedInputStream,緩存大小爲size
    public CountingInputStream(InputStream in, int size) {
        super(in, size);
    }
    //構造一個BufferedInputStream,緩存大小爲8192bytes
    public CountingInputStream(InputStream in) {
        super(in);
    }
    //獲得當前讀取文件的字節數
    public long getBytesRead() {
        return bytesRead;
    }
    //每次讀取一個字節,並將讀取字節數加1
    //使用synchronized關鍵字,使該函數同時只能被同一個實例調用一次
    public synchronized int read() throws IOException {
        int read = super.read();
        if (read >= 0) {
            bytesRead++;
        }
        return read;
    }
    //讀取不超過len長度的字節,存儲在buffer b中從偏移量爲off開始的位置,
    //read爲實際讀取到的字節數,並將當前讀取本身數加上read
    public synchronized int read(byte[] b, int off, int len) throws IOException {

        int read = super.read(b, off, len);
        if (read >= 0) {
            bytesRead += read;
        }
        return read;
    }
    //跳過最多n字節,返回實際跳過的字節數,並將當前字節數加skipped
    public synchronized long skip(long n) throws IOException {

        long skipped = super.skip(n);
        if (skipped >= 0) {
            bytesRead += skipped;
        }
        return skipped;
    }
    //將當前位置保存到bytesReadMark這個變量中
    //若讀取偏移量超過readlimit,則該mark的位置失效
    public synchronized void mark(int readlimit) {
        super.mark(readlimit);
        bytesReadMark = bytesRead;
    }
    //將當前文件位置恢復到bytesReadMark所指的位置
    //若是文件關閉,或者沒有mark一個位置,或者以前mark的位置已經失效,將會拋出一個IOException
    public synchronized void reset() throws IOException {
        super.reset();
        bytesRead = bytesReadMark;
    }
}
//數據文件下載,主要是負責解壓縮data.zip文件
class DataDownloader extends Thread
{
    public StatusWriter Status;//用於顯示進度,額外信息等
    public boolean DownloadComplete = false;
    public boolean DownloadFailed = false;
    private MainActivity Parent;//用於傳遞主界面類,以進行界面相關操做
    private String outFilesDir = null;//輸出文件目錄
    class StatusWriter
    {
        private ProgressDialog Status;    //用於顯示信息
        private MainActivity Parent;//用於與主界面交互
        private String oldText = "";
        //實例化成員變量,將主界面元素傳遞過來,方便進行更新
        public StatusWriter( ProgressDialog _Status, MainActivity _Parent )
        {
            Status = _Status;
            Parent = _Parent;
        }
        public void setParent( ProgressDialog _Status, MainActivity _Parent )
        {
            //鎖定一個對象,當是同一個DataDownloader實例時是線程同步的
            //可是不一樣實例仍是不一樣步,想要不一樣對象也同步,這個對象必須是靜態對象
            synchronized(DataDownloader.this) {
                Status = _Status;
                Parent = _Parent;
                setText( oldText );//初始化的時候TextView顯示空
            }
        }
        
        public void setText(final String str)
        {
            //用於更新TextView中的內容
            class Callback implements Runnable
            {
                public ProgressDialog Status;
                public String text;
                public void run()
                {
                    Status.setMessage(text);
                }
            }
            synchronized(DataDownloader.this) {
                Callback cb = new Callback();
                oldText = new String(str);
                cb.text = new String(str);
                cb.Status = Status;
                //爲了防止程序崩潰,價格判斷是值得的
                if( Parent != null && Status != null )
                    Parent.runOnUiThread(cb);//運行在UI線程中,以更新界面元素
                
            }
        }
        
    }
    //類DataDownloader的構造函數,傳入主界面相應的元素,爲更新界面信息做準備
    public DataDownloader( MainActivity _Parent, ProgressDialog _Status )
    {
        Parent = _Parent;
        Status = new StatusWriter( _Status, _Parent );//構建StatusWriter類,專門用於進行界面的更新操做
        //Status.setText( "Connecting to " + Globals.DataDownloadUrl );
        outFilesDir = Globals.DataDir;//初始化目標目錄的路徑
        DownloadComplete = false;    //初始化DownloadComplete
        this.start();        //運行該類的核心函數
    }
    
    public void setStatusField(ProgressDialog _Status)
    {
        synchronized(this) {
            Status.setParent( _Status, Parent );
        }
    }
    //核心函數
    @Override
    public void run()
    {    
        //檢查目標目錄的文件是否完整和正確,傳入壓縮文件名和要檢查的標識文件名
        if( ! DownloadDataFile(Globals.DataDownloadUrl, "DownloadFinished.flag") )
        {
            DownloadFailed = true;
            return;
        }
        //若是運行到了這裏,說明數據是正確的
        DownloadComplete = true;
        //初始化
        initParent();
    }

    public boolean DownloadDataFile(final String DataDownloadUrl, final String DownloadFlagFileName)
    {    
        //初始化資源實例
        Resources res = Parent.getResources();
        //檢查目標文件是否包含指定的數據
        String path = getOutFilePath(DownloadFlagFileName);
        InputStream checkFile = null;
        try {
            checkFile = new FileInputStream( path );
        } catch( FileNotFoundException e ) {
        } catch( SecurityException e ) { };
        if( checkFile != null )
        {
            try {
                //構造一個比標準數據稍大的buffer,用於存儲文件數據
                byte b[] = new byte[ Globals.DataDownloadUrl.getBytes("UTF-8").length + 1 ];
                int readed = checkFile.read(b);
                String compare = new String( b, 0, readed, "UTF-8" );  //DataDownloadUrl=data.zip
                boolean matched = false;
                //若compare=data.zip
                if( compare.compareTo(DataDownloadUrl) == 0 )
                    matched = true;
                //若是不匹配,拋出異常,直接跳轉到1所在的位置
                if( ! matched )
                    throw new IOException();
                Status.setText( res.getString(R.string.download_unneeded) );
                return true;
            } catch ( IOException e ) {};
        }
        checkFile = null;  //----1
        //mkdirs 將會產生全部中間必要的目錄
        try {
            (new File( outFilesDir )).mkdirs();
            OutputStream out = new FileOutputStream( getOutFilePath(".nomedia") );
            out.flush();
            out.close();
        }
        catch( SecurityException e ) {}
        catch( FileNotFoundException e ) {}
        catch( IOException e ) {};

        long totalLen = 0;
        CountingInputStream stream;
        byte[] buf = new byte[16384];

        String url = DataDownloadUrl;
        System.out.println("Unpacking from assets: '" + url + "'");
        try {
            //打開assets下的文件
            stream = new CountingInputStream(Parent.getAssets().open(url), 8192);
            //跳到文件末尾以肯定文件大小
            while( stream.skip(65536) > 0 ) { };
            //將文件大小存儲到totalLen
            totalLen = stream.getBytesRead();
            stream.close();
            //從新打開文件,以重置文件當前讀取位置
            stream = new CountingInputStream(Parent.getAssets().open(url), 8192);
            
        } catch( IOException e ) {
            System.out.println("Unpacking from assets '" + url + "' - error: " + e.toString());
            Status.setText( res.getString(R.string.error_dl_from, url) );//載入出錯
            return false;
        }
        ZipInputStream zip = new ZipInputStream(stream);
        
        while(true)
        {
            ZipEntry entry = null;
            try {
                //取得壓縮文件的一個個子元素
                entry = zip.getNextEntry();
                if( entry != null )
                    System.out.println("Reading from zip file '" + url + "' entry '" + entry.getName() + "'");
            } catch( java.io.IOException e ) {
                Status.setText( res.getString(R.string.error_dl_from, url) );
                System.out.println("Error reading from zip file '" + url + "': " + e.toString());
                return false;
            }
            //若是元素爲空,代表壓縮文件已經讀取完畢
            if( entry == null )
            {
                System.out.println("Reading from zip file '" + url + "' finished");
                break;
            }
            //若是是目錄,則建立相應目錄結構,接着直接讀取下一個元素
            if( entry.isDirectory() )
            {
                System.out.println("Creating dir '" + getOutFilePath(entry.getName()) + "'");
                try {
                    (new File( getOutFilePath(entry.getName()) )).mkdirs();
                } catch( SecurityException e ) { };
                continue;
            }

            OutputStream out = null;
            path = getOutFilePath(entry.getName());
            //安全起見,建立目標文件所須要的目錄結構,雖然這裏不須要,但之後可能會須要
            try {
                (new File( path.substring(0, path.lastIndexOf("/") ))).mkdirs();
            } catch( SecurityException e ) { };
            
            try {
                //使用CRC32校驗目標文件
                CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() );
                while( check.read(buf, 0, buf.length) > 0 ) {};
                check.close();
                //檢查校驗和是否正確
                if( check.getChecksum().getValue() != entry.getCrc() )
                {
                    File ff = new File(path);
                    ff.delete();//校驗和不正確則刪除文件,從新建立文件
                    throw new Exception(); //跳轉到catch
                }
                System.out.println("File '" + path + "' exists and passed CRC check - not overwriting it");
                //若校驗成功,則繼續校驗下一個文件
                continue;
            } catch( Exception e )
            {
            }
            try {
                out = new FileOutputStream( path );
            } catch( FileNotFoundException e ) {
                System.out.println("Saving file '" + path + "' - cannot create file: " + e.toString());
            } catch( SecurityException e ) {
                System.out.println("Saving file '" + path + "' - cannot create file: " + e.toString());
            };
            //若是建立文件失敗
            if( out == null )
            {
                Status.setText( res.getString(R.string.error_write, path) );
                System.out.println("Saving file '" + path + "' - cannot create file");
                return false;
            }
            //將檢查結果以百分比顯示到progress dialog中
            float percent = 0.0f;
            if( totalLen > 0 )
                percent = stream.getBytesRead() * 100.0f / totalLen;
            Status.setText( res.getString(R.string.dl_progress, percent, path) );
            
            try {
                //讀取當前元素
                int len = zip.read(buf);
                while (len >= 0)
                {
                    if(len > 0)
                        out.write(buf, 0, len);//將當前元素的內容拷貝到目標文件
                    len = zip.read(buf);

                    percent = 0.0f;
                    if( totalLen > 0 )
                        percent = stream.getBytesRead() * 100.0f / totalLen;
                    Status.setText( res.getString(R.string.dl_progress, percent, path) );
                }
                out.flush();
                out.close();
                out = null;
            } catch( java.io.IOException e ) {
                Status.setText( res.getString(R.string.error_write, path) );
                System.out.println("Saving file '" + path + "' - error writing or downloading: " + e.toString());
                return false;
            }
            
            try {
                //拷貝完以後一樣還要校驗一下
                CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() );
                while( check.read(buf, 0, buf.length) > 0 ) {};
                check.close();
                if( check.getChecksum().getValue() != entry.getCrc() )
                {
                    File ff = new File(path);
                    ff.delete();
                    throw new Exception();
                }
            } catch( Exception e )
            {
                Status.setText( res.getString(R.string.error_write, path) );
                System.out.println("Saving file '" + path + "' - CRC check failed");
                return false;
            }
            System.out.println("Saving file '" + path + "' done");
        }

        OutputStream out = null;
        //所有完成以後將制定信息寫入校驗文件中
        path = getOutFilePath(DownloadFlagFileName);
        try {
            out = new FileOutputStream( path );
            out.write(DataDownloadUrl.getBytes("UTF-8"));
            out.flush();
            out.close();
        } catch( FileNotFoundException e ) {
        } catch( SecurityException e ) {
        } catch( java.io.IOException e ) {
            Status.setText( res.getString(R.string.error_write, path) );
            return false;
        };
        Status.setText( res.getString(R.string.dl_finished) );

        try {
            stream.close();
        } catch( java.io.IOException e ) {
        };

        return true;
    };
    //若前面的數據都正確,則執行界面的初始化,將數據文件以列表的形式呈現出來
    private void initParent()
    {
        class Callback implements Runnable
        {
            public MainActivity Parent;
            public void run()
            {
                Parent.getFileList();//得到目標目錄的文件列表
                Log.e("guojs","initParent!");
            }
        }
        Callback cb = new Callback();
        synchronized(this) {
            cb.Parent = Parent;//傳入主界面類的實例
            if(Parent != null)
                Parent.runOnUiThread(cb);//由於須要更新界面,所以須要運行在UI線程
        }
    }
    //返回目標輸出文件的絕對路徑
    private String getOutFilePath(final String filename)
    {
        return outFilesDir + "/" + filename;
    };
    

}java

相關文章
相關標籤/搜索