2010-05-18 110 views
36

我疯了,我创建了一个文件对象,所以可以用ObjectInputStream读取它,然后我放置了资源文件夹。 该方法适用于小于1M的文件,并在较大的文件中出错。 我读到这是Android平台的限制,但我也知道可以“轻松”避免。例如,那些已经下载了游戏Reging Thunder的人,可以很容易地看到在他们的资产文件夹中有一个18.9M大的文件。 这是一个ObjecInputStream从资产文件夹加载大于1M的文件

File f = File.createTempFile("mytempfile", "dat"); 
FileOutputStream fos = new FileOutputStream(f); 

InputStream is = mc.getAssets().open(path,3); 

ObjectInputStream ois=new ObjectInputStream(is); 
byte[] data = (byte[]) ois.readObject(); 
fos.write(data); 

fos.flush(); 
fos.close(); 
ois.close(); 
is.close(); 

我读1对象代码,现在我有一个未压缩的文件,我可以使用它,而不用担心错误“此文件无法打开的文件描述符;它可能是压缩“

此函数适用于小于1M的文件,较大的文件返回 在”ObjectInputStream ois = new ObjectInputStream(is);“上的java.io.IOException异常”

为什么?

回答

48

面对同样的问题。我已经将我的4MB文件分割为1 MB块,并且在第一次运行时,我会将块加入到手机上的数据文件夹中。作为额外的好处,APK已被正确压缩。该块文件被称为1.db,2.db等的代码是这样的:

File Path = Ctxt.getDir("Data", 0); 
File DBFile = new File(Path, "database.db"); 

if(!DBFile.exists() || DatabaseNeedsUpgrade) //Need to copy... 
    CopyDatabase(Ctxt, DBFile); 


static private void CopyDatabase(Context Ctxt, File DBFile) throws IOException 
{ 
    AssetManager assets = Ctxt.getAssets(); 
    OutputStream outstream = new FileOutputStream(DBFile); 
    DBFile.createNewFile(); 
    byte []b = new byte[1024]; 
    int i, r; 
    String []assetfiles = assets.list(""); 
    Arrays.sort(assetfiles); 
    for(i=1;i<10;i++) //I have definitely less than 10 files; you might have more 
    { 
     String partname = String.format("%d.db", i); 
     if(Arrays.binarySearch(assetfiles, partname) < 0) //No such file in assets - time to quit the loop 
      break; 
     InputStream instream = assets.open(partname); 
     while((r = instream.read(b)) != -1) 
      outstream.write(b, 0, r); 
     instream.close(); 
    } 
    outstream.close(); 
} 
+1

感谢您的答案,但您使用哪种工具来削减您的大型分贝到每个较小的块? – anticafe 2010-11-23 03:19:07

+0

尝试了几个现成的免费软件,然后写了另一个:)在linux和mac上,这是一个20行的C代码。 – 2010-11-23 14:56:11

+6

有一个名为split的命令行工具。 – mclin 2011-01-28 08:22:48

0

而不是assets文件夹,我把我的大文件放在原始文件夹中。这个对我有用。

+0

我总是有同样的问题: “数据超出UNCOMPRESS_DATA_MA X(1314625 vs 1048576)“ 无论如何谢谢 – Syco 2010-05-19 13:29:58

+0

我使用Eclipse和我的应用程序Navkar Matra http://www.androlib.com/android.application.com-app-navkarmantra-Cxin。aspx在其中有一个2MB的文件,效果非常好。 – the100rabh 2010-05-21 06:57:39

32

限制是压缩资产。如果资产未压缩,则系统可以对文件数据进行存储映射,并使用Linux虚拟内存分页系统适当地拉入或丢弃4K块。 (“zipalign”工具确保未压缩的资产在文件中是字对齐的,这意味着它们在直接映射时也会在内存中对齐。)

如果资产是压缩的,系统必须解压缩整个记忆的东西。如果您拥有20MB的资源,那意味着20MB的物理内存被您的应用程序捆绑在一起。

理想情况下,系统会采用某种窗口化的压缩方式,这样只需要存在部分内容,但这需要资产API中的一些幻想,以及与随机访问配合使用的压缩方案。现在APK == Zip和“deflate”压缩,所以这是不实际的。

您可以通过为它们提供未压缩的文件类型后缀(例如“.png”或“.mp3”)来保持资源未压缩。您也可以在构建过程中使用“zip -0”手动添加它们,而不是将它们捆绑在aapt中。这可能会增加APK的大小。

+0

我今天刚刚面对这个问题,可以通过将文件名后缀更改为.mp3来达到目的。虽然.png不起作用。 AssetManager说它找不到该文件:(非常感谢你为后缀提示! – 2011-03-22 14:40:01

+2

这[post](http://stackoverflow.com/questions/3177026/problem-while-opening-asset-file-with-帮助内容提供商)有一个这样的扩展名列表 – Samuel 2011-04-11 00:32:58

-1

我使用NetBeans构建包,但未找到如何更改AAPT的设置。 我没有尝试PNG,但MP3被压缩。 我可以编译软件包,然后用参数-0输入assets文件夹? 什么是正确的命令使用?

7

UNE梅索德PAS黑白配consiste一换extention杜fichier TTF一个MP3

+14

译文:把文件扩展名改为.mp3 – eggie5 2011-08-04 05:49:06

+0

不错的工作yassine,但是用这个解决方案,playStore没有问题(当我在playStore上载apk时) – 2013-07-10 11:37:50

8

像舍瓦建议你可以将文件分成大块。我用这个分裂我的4MB的文件

public static void main(String[] args) throws Exception { 
    String base = "tracks"; 
    String ext = ".dat"; 
    int split = 1024 * 1024; 
    byte[] buf = new byte[1024]; 
    int chunkNo = 1; 
    File inFile = new File(base + ext); 
    FileInputStream fis = new FileInputStream(inFile); 
    while (true) { 
     FileOutputStream fos = new FileOutputStream(new File(base + chunkNo + ext)); 
     for (int i = 0; i < split/buf.length; i++) { 
     int read = fis.read(buf); 
     fos.write(buf, 0, read); 
     if (read < buf.length) { 
      fis.close(); 
      fos.close(); 
      return; 
     } 
     } 
     fos.close(); 
     chunkNo++; 
    } 
    } 

如果您不需要的文件再次合并到设备上的单个文件,只需使用InputStream的,这将它们合并到一个上飞。

import java.io.IOException; 
import java.io.InputStream; 

import android.content.res.AssetManager; 

public class SplitFileInputStream extends InputStream { 

    private String baseName; 
    private String ext; 
    private AssetManager am; 
    private int numberOfChunks; 
    private int currentChunk = 1; 
    private InputStream currentIs = null; 

    public SplitFileInputStream(String baseName, String ext, int numberOfChunks, AssetManager am) throws IOException { 
    this.baseName = baseName; 
    this.am = am; 
    this.numberOfChunks = numberOfChunks; 
    this.ext = ext; 
    currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING); 
    } 

    @Override 
    public int read() throws IOException { 
    int read = currentIs.read(); 
    if (read == -1 && currentChunk < numberOfChunks) { 
     currentIs.close(); 
     currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING); 
     return read(); 
    } 
    return read; 
    } 

    @Override 
    public int available() throws IOException { 
    return currentIs.available(); 
    } 

    @Override 
    public void close() throws IOException { 
    currentIs.close(); 
    } 

    @Override 
    public void mark(int readlimit) { 
    throw new UnsupportedOperationException(); 
    } 

    @Override 
    public boolean markSupported() { 
    return false; 
    } 

    @Override 
    public int read(byte[] b, int offset, int length) throws IOException { 
    int read = currentIs.read(b, offset, length); 
    if (read < length && currentChunk < numberOfChunks) { 
     currentIs.close(); 
     currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING); 
     read += read(b, offset + read, length - read); 
    } 
    return read; 
    } 

    @Override 
    public int read(byte[] b) throws IOException { 
    return read(b, 0, b.length); 
    } 

    @Override 
    public synchronized void reset() throws IOException { 
    if (currentChunk == 1) { 
     currentIs.reset(); 
    } else { 
     currentIs.close(); 
     currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING); 
     currentChunk = 1; 
    } 
    } 

    @Override 
    public long skip(long n) throws IOException { 
    long skipped = currentIs.skip(n); 
    if (skipped < n && currentChunk < numberOfChunks) { 
     currentIs.close(); 
     currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING); 
     skipped += skip(n - skipped); 
    } 
    return skipped; 
    } 
} 

用法:
ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));

+0

你的方法似乎工作但在实践中我遇到了两个问题。第一种方法(主 - java - 分割器方法)当文件长度恰好为1024 * n(n> 0)时,我面临问题。在读完最后一个字节后,'int read'的值为'-1','fos.write'失败。我改变了写入机制,如'if(read> -1){fos.write(buf,0,read); }”。下一个问题属于'SplitFileInputStream'类。方法'public int read(byte [] b)'不能很好地工作。所以我改变它类似于'public int read(byte [] b,int offset,int length)'方法。 – zmeda 2011-06-14 13:09:15

+0

它看起来像'public int read(byte [] b)抛出IOException异常{ \t \t int read = currentIs.read(b); (read zmeda 2011-06-14 13:12:12

2

我知道这是一个老问题,但我认为一个好的解决方案。 为什么不将文件预先存储在资产文件夹中。 然后,因为它已经是一个zip文件,因此压缩它不需要再次压缩。所以,如果你想要压缩文件来减小你的apk的大小,但你不想处理分割文件,我认为这很容易。

当你需要阅读该文件关闭设备只是包装InputStream的在zipinputstream http://developer.android.com/reference/java/util/zip/ZipInputStream.html

+0

这里是代码示例如何打开zip文件而不是gz文件https://github.com/jgilfelt/android-sqlite-asset-helper/blob/master/library/src/main/java/com/ readystatesoftware/sqliteasset/SQLiteAssetHelper.java#L449这似乎适用于大于1M的文件。 – 2017-12-11 18:49:54

0

添加文件扩展名是mp3.I使用mydb.mp3in资产文件夹,复制。这不运行检查error.show它。

+0

谢谢你队友这是非常简单的工作 – Jack 2015-06-02 08:25:50

2

我找到了另一个解决方案,也许你对它感兴趣。

在你的源代码,在那里你有build.xml文件中的根,你可以在custom_rules.xml文件,该文件是用于添加/修改Ant目标没有标准的Android应用程序的构建系统破坏任何东西覆盖-package-resources目标。

只需创建一个文件与此内容:

<?xml version="1.0" encoding="UTF-8"?> 
<project name="yourAppHere" default="help"> 

    <target name="-package-resources" depends="-crunch"> 
     <!-- only package resources if *not* a library project --> 
     <do-only-if-not-library elseText="Library project: do not package resources..." > 
      <aapt executable="${aapt}" 
        command="package" 
        versioncode="${version.code}" 
        versionname="${version.name}" 
        debug="${build.is.packaging.debug}" 
        manifest="${out.manifest.abs.file}" 
        assets="${asset.absolute.dir}" 
        androidjar="${project.target.android.jar}" 
        apkfolder="${out.absolute.dir}" 
        nocrunch="${build.packaging.nocrunch}" 
        resourcefilename="${resource.package.file.name}" 
        resourcefilter="${aapt.resource.filter}" 
        libraryResFolderPathRefid="project.library.res.folder.path" 
        libraryPackagesRefid="project.library.packages" 
        libraryRFileRefid="project.library.bin.r.file.path" 
        previousBuildType="${build.last.target}" 
        buildType="${build.target}" 
        ignoreAssets="${aapt.ignore.assets}"> 
       <res path="${out.res.absolute.dir}" /> 
       <res path="${resource.absolute.dir}" /> 
       <nocompress /> <!-- forces no compression on any files in assets or res/raw --> 
       <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw --> 
      </aapt> 
     </do-only-if-not-library> 
    </target> 
</project> 
0

使用GZIP将是另一种方法。您只需要将InputStream内部GZIPInputStream

我用这个数据库的大小约为3.0 MB,输出压缩文件大约为600KB。

  • 对于杉杉运行复制DB,我使用GZIP tool gzip压缩我的源.db文件。
  • 然后将其重命名为.jpg以避免更多压缩 (这些过程在编译APK文件之前完成)。
  • 然后从assetss

读取压缩GZIP文件,并将其复制:

private void copydatabase() throws IOException { 
     // Open your local db as the input stream 
     InputStream myinput = mContext.getAssets().open(DB_NAME_ASSET); 
     BufferedInputStream buffStream = new BufferedInputStream(myinput); 
     GZIPInputStream zis = new GZIPInputStream(buffStream); 

     // Path to the just created empty db 
     String outfilename = DB_PATH + DB_NAME; 

     // Open the empty db as the output stream 
     OutputStream myoutput = new FileOutputStream(outfilename); 


     // transfer byte to inputfile to outputfile 
     byte[] buffer = new byte[1024]; 
     int length; 
     while ((length = zis.read(buffer)) > 0) { 
      myoutput.write(buffer, 0, length); 
     } 

     // Close the streams 
     myoutput.flush(); 
     myoutput.close(); 
     zis.close(); 
     buffStream.close(); 
     myinput.close(); 
    } 
相关问题