【问题标题】:Android Java: Reading file from zip file into webview/string, Why ZipInputStream limits read performance?Android Java:将 zip 文件中的文件读取到 webview/string,为什么 ZipInputStream 会限制读取性能?
【发布时间】:2014-03-07 20:10:40
【问题描述】:

我们先解释一下

我正在使用 webView 加载 HTML 应用程序。为了稍微保护源代码(脚本小子保护),我想从(受保护的)zip 文件中加载 html 代码。 html 代码已经打包、缩小、组合等,因此只有 700kb 大小(未打包)。这工作得很好,但有一个问题,它有点慢。

在下面的示例中,我从 zip 中读取 html 文件并将结果放入字符串中。此字符串将用于通过使用以下代码将 html 代码加载到 webview 中:

this.webView.loadDataWithBaseURL("file:///android_asset/", this.unzipStream(sFileName), "text/html", "UTF-8", "");  

我尝试了不同的解决方案来加快速度,并发现 瓶颈在于从 zip 中读取内容。我增加了读取缓冲区的块大小,但没有帮助,它永远不会读取完整的块大小。例如,当我使用 4096 字节 (4kb) 作为块大小时,它一次只能读取 700 到 1100 个字节。

问题:

  • 如何强制读取函数使用指定的完整块大小?
  • 否则,有没有更好的方法(比如直接放到webview中)?

这是我制作的代码:

   public String unzipStream( String sFileName ) 
    { 
        final int BLOCKSIZE = 4096;
        //String sResult = ""; 
        long iSize   = 0;
        int iReaded = 0;
        ByteArrayOutputStream sb = new ByteArrayOutputStream(); 

        try  { 
          InputStream is = this.activity.getAssets().open( sFileName );
          BufferedInputStream fin = new BufferedInputStream( is ); 
          ZipInputStream zin = new ZipInputStream(fin); 
          ZipEntry ze;

          while( (iSize == 0) && ((ze = zin.getNextEntry()) != null) && !ze.isDirectory() ) 
          {
               byte data[]  = new byte[BLOCKSIZE];
               long iTotal  = ze.getSize();

               while ((iReaded = zin.read(data,0,BLOCKSIZE)) > 0 && ((iSize+=iReaded) <= iTotal) ) 
               {   
                   sb.write(data,0,iReaded);
               }
               zin.closeEntry(); 
          } 

          zin.close(); 
        } 
        catch(Exception e) 
        { 
             System.out.println("Error unzip: "+e.getMessage());
             //sResult = "";
             iSize = 0;
        } 

        if( iSize > 0 )
        {
            //Base64.
            try {
                return sb.toString("UTF-8");
                //sResult = new String( Base64.decode(sb.toString("UTF-8"), Base64.DEFAULT), Charset.forName("UTF-8") );
            }
            catch(Exception ee)
            {
               //sResult = "";
            }
        }

        return "";
      } 

也许是另一种方法:

还发现了这个 java zipfile 类http://www.lingala.net/zip4j/。它使处理(受密码保护的)zip 文件变得更容易,但不包括将其解压缩为字符串的功能(至少我认为如此)。搜索时什么也没找到。这门课有可能吗?

【问题讨论】:

    标签: java android zip android-webview unzip


    【解决方案1】:

    之后,标题可能是“ZipInputStream 限制读取性能”或类似的标题,因为其他类型的流不会限制读取大小。当您想获得 4096 字节时,您将获得 4096 字节。例如,使用文本文件对此进行了测试。我仍然不知道为什么 ZipInputStream 会限制读取性能。

    我不太确定是否有真正的性能提升(有时会,有时不会),但现在使用来自 apache 的“Commons IO”包 - http://commons.apache.org/proper/commons-io/ 的 IOUtils。这也简化了整个“操作”。

    我也看到了一些带有通道的解决方案,但似乎只适用于文件流,因此无法使用它(或者无法弄清楚如何将其应用于这种情况)。 另请参阅:Faster way of copying data in Java?(请参阅已接受的答案)。

    这是我制作的新版本(也将对象名称更改为有意义的名称):

    public String unzipStream( String sFileName ) 
    { 
        ByteArrayOutputStream oBaosBuffer = new ByteArrayOutputStream(); 
        try  
        { 
          ZipInputStream oZipStream = new ZipInputStream( this.activity.getAssets().open( sFileName ) ); 
          ZipEntry oZipEntry;
          long iSize = 0;
    
          while( (iSize == 0) && ((oZipEntry = oZipStream.getNextEntry()) != null) && !oZipEntry.isDirectory() ) 
          {
               iSize = IOUtils.copyLarge(oZipStream, oBaosBuffer);
               oZipStream.closeEntry(); 
          } 
    
          oZipStream.close(); 
    
          if( iSize > 0 )
          {
            return oBaosBuffer.toString("UTF-8");
            //sResult = new String( Base64.decode(sb.toString("UTF-8"), Base64.DEFAULT), Charset.forName("UTF-8") );
          }
        } 
        catch(Exception e) 
        { 
             System.out.println("Error unzip: "+e.getMessage());
        } 
    
        return null;
    } 
    

    【讨论】:

    • 还可以查看 marcin.kosiba 的解决方案/答案以将两者结合起来。
    【解决方案2】:

    使用 shouldInterceptRequest 代替 loadDataWithBaseUrl 可能会略微提升性能。这不会解决您的块大小问题,但它会允许 WebKit 在您完成解压缩整个内容之前开始解析您的内容。

    你会使用它的方式是:

    class MyWebViewClient extends WebViewClient {
    @Override
    public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
        // this method is *not* called on the UI thread, be careful to not touch UI classes.
        if (Uri.parse(url).getHost().equals(Uri.parse("file:///android_asset/..."))) {
            // This assumes you pass in the AssetManager to the MyWebViewClient constructor.
            InputStream is = assetManager.open(sFileName);
            ZipInputStream zipInputStream = new ZipInputStream(is); 
            return new WebResourceResponse("text/html", "UTF-8", zipInputStream);
        }
        return super.shouldInterceptRequest(view, url);
    }
    

    【讨论】:

    • 看起来很有趣很快就会尝试。
    【解决方案3】:

    尝试设置流的内部缓冲区大小:

        BufferedInputStream fin = new BufferedInputStream(is,  BLOCKSIZE); 
        ZipInputStream zin = new ZipInputStream(fin){ { buf = new byte[BLOCKSIZE]; }  }; 
    

    【讨论】:

    • 您好,感谢您的回答,但不会加快任何速度。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 2013-07-05
    • 2020-12-04
    相关资源
    最近更新 更多