Android 从资产文件夹加载大于 1M 的文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2860157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Load files bigger than 1M from assets folder
提问by Syco
I'm going crazy, I created a file object, so it can be read with ObjectInputStream, and I placed the assets folder. The method works with a file smaller than 1M, and give error with larger files. I read that is a limit of Android platform, but I also know that can be "easily" avoided. Those who have downloaded the game Reging Thunder, for example, can easily see that in their assets folder is a file 18.9M large. This is my code for read 1 object from a ObjecInputStream
我快疯了,我创建了一个文件对象,所以它可以用ObjectInputStream读取,然后我放置了assets文件夹。该方法适用于小于 1M 的文件,并在文件较大时出错。我读到这是 Android 平台的限制,但我也知道可以“轻松”避免。例如,那些下载过雷霆迅雷游戏的人,可以很容易地看到他们的assets文件夹中有一个18.9M大的文件。这是我从 ObjecInputStream 读取 1 个对象的代码
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();
now I have an uncompressed file and I can use it without worrying about the error "This file can not be opened as a file descriptor; it is probably compressed"
现在我有一个未压缩的文件,我可以使用它而不必担心错误“此文件无法作为文件描述符打开;它可能已压缩”
This function works well with files smaller than 1M, with bigger files return an java.io.IOException on line "ObjectInputStream ois=new ObjectInputStream(is);"
此函数适用于小于 1M 的文件,较大的文件在“ObjectInputStream ois=new ObjectInputStream(is);”行返回 java.io.IOException
why??
为什么??
回答by Seva Alekseyev
Faced the same issue. I've cut up my 4MB file into 1 MB chunks, and on the first run I join the chunks into a data folder on the phone. As an added bonus, the APK is properly compressed. The chunk files are called 1.db, 2.db, etc. The code goes like this:
面临同样的问题。我已经将我的 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();
}
回答by fadden
The limitation is on compressed assets. If the asset is uncompressed, the system can memory-map the file data and use the Linux virtual memory paging system to pull in or discard 4K chunks as appropriate. (The "zipalign" tool ensures that uncompressed assets are word-aligned in the file, which means they'll also be aligned in memory when directly mapped.)
限制是压缩资产。如果资产是未压缩的,系统可以对文件数据进行内存映射,并使用 Linux 虚拟内存分页系统适当地拉入或丢弃 4K 块。(“zipalign”工具确保未压缩的资产在文件中按字对齐,这意味着它们在直接映射时也会在内存中对齐。)
If the asset is compressed, the system has to uncompress the entire thing to memory. If you have a 20MB asset, that means 20MB of physical memory is tied up by your application.
如果资产被压缩,系统必须将整个事物解压缩到内存中。如果您有 20MB 的资产,这意味着您的应用程序占用了 20MB 的物理内存。
Ideally the system would employ some sort of windowed compression, so that only parts need to be present, but that requires some fanciness in the asset API and a compression scheme that works well with random access. Right now APK == Zip with "deflate" compression, so that's not practical.
理想情况下,系统将采用某种窗口压缩,因此只需要存在部分,但这需要资产 API 和压缩方案中的一些奇思妙想,以及适用于随机访问的压缩方案。现在 APK == Zip 带有“放气”压缩,所以这是不切实际的。
You can keep your assets uncompressed by giving them a suffix of a file type that doesn't get compressed (e.g. ".png" or ".mp3"). You may also be able to add them manually during the build process with "zip -0" instead of having them bundled up by aapt. This will likely increase the size of your APK.
您可以通过给您的资产一个未压缩的文件类型的后缀(例如“.png”或“.mp3”)来保持您的资产未压缩。您也可以在构建过程中使用“zip -0”手动添加它们,而不是通过 aapt 将它们捆绑起来。这可能会增加您的 APK 的大小。
回答by Oliver
Like Seva suggested you can split up your file in chunks. I used this to split up my 4MB file
就像 Seva 建议的那样,您可以将文件分成块。我用它来分割我的 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++;
}
}
If you do not need to combine the files into a single file on the device again, just use this InputStream, which combines them into one on the fly.
如果您不需要再次在设备上将这些文件合并为一个文件,只需使用这个 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;
}
}
Usage:ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));
用法:ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));
回答by yassine
Change the file extension to .mp3
将文件扩展名更改为 .mp3
回答by Matt Wolfe
I know this is an old question but I thought of a good solution. Why not store the file already pre-zipped in the asset folder. Then since it is already a zip file and therefore compressed it won't need to be compressed again. So if you wanted the file to be compressed to decrease the size of your apk but you don't want to deal with splitting up files I think this is easier.
我知道这是一个老问题,但我想到了一个很好的解决方案。为什么不将已经预压缩的文件存储在资产文件夹中。然后,由于它已经是一个 zip 文件,因此已压缩,因此不需要再次压缩。因此,如果您希望压缩文件以减小 apk 的大小,但又不想处理拆分文件,我认为这更容易。
When you need to read that file off the device just wrap the inputstream in a zipinputstream http://developer.android.com/reference/java/util/zip/ZipInputStream.html
当您需要从设备读取该文件时,只需将输入流包装在 zipinputstream http://developer.android.com/reference/java/util/zip/ZipInputStream.html
回答by Ottavio Campana
I found another solution, maybe you're interested in it.
我找到了另一个解决方案,也许你对它感兴趣。
In the root of your sources, where you have the build.xml
file, you can overwrite the -package-resources
target in the custom_rules.xml
file, which is used for adding/modifying targets in ant without breaking anything in the standard android app build system.
在您拥有build.xml
文件的源的根目录中,您可以覆盖文件中的-package-resources
目标,该目标custom_rules.xml
用于在 ant 中添加/修改目标,而不会破坏标准 android 应用程序构建系统中的任何内容。
Just create a file with this content:
只需创建一个包含此内容的文件:
<?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>
回答by VSB
Using GZIPwould be another method. you only need to wrap InputStreaminside GZIPInputStream.
I used this for a database which size about 3.0 MB and output compress file was about 600KB.
使用GZIP将是另一种方法。您只需要将InputStream包装在GZIPInputStream 中。
我将此用于大小约为 3.0 MB 的数据库,输出压缩文件约为 600 KB。
- For copying DB in firs run, I gzipped my source .db file using GZIP tool.
- Then renamed it to .jpg in order to avoid more compression (these processes are done before compile APK FILE).
- Then for reading compressed GZIP file from assetss
- 为了在第一次运行中复制数据库,我使用GZIP 工具压缩了我的源 .db 文件。
- 然后将其重命名为 .jpg 以避免更多压缩(这些过程在编译 APK FILE 之前完成)。
- 然后从资产中读取压缩的 GZIP 文件
and copying it:
并复制它:
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();
}
回答by Ehsan Jelodar
set database to multiple part with a program e.g "Win Hex", you can download from Link
使用程序将数据库设置为多个部分,例如“Win Hex”,您可以从链接下载
and continue Load files bigger than 1M from assets folder
回答by santosh Kundkar
add file extension is mp3.I use mydb.mp3in assets folder and copy .this run without error.show check it.
添加文件扩展名是 mp3。我使用 mydb.mp3in assets 文件夹并复制 .this run without error.show 检查它。
回答by the100rabh
Instead of assets folder, I have put my large files in the raw folder. It works for me.
我将大文件放在原始文件夹中,而不是资产文件夹。这个对我有用。