2010-05-18 64 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(); 
    } 
相關問題