有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在安卓应用程序上读取大型Base64文件(150MB)?

我正在尝试在安卓应用程序上读取大小为(~ 150MB)的大型base64文本文件

该文件包含JSON字符串,我需要将其解码并转换为JSON对象,然后在应用程序中使用它。问题是我在尝试读取此数据时遇到异常Out of Memory

该应用程序需要脱机工作,因此我需要下载完整数据

代码如下:

    String localPath = getApplicationContext().getFilesDir().getPath().toString() ;
    String key = "dataFile.txt" ;

    StringBuilder text = new StringBuilder();
    File file=new File(localPath+"/"+ key);

    byte fileContent[] = new byte[3000];

    try ( FileInputStream fin = new FileInputStream(file)) {
        while(fin.read(fileContent) >= 0) {
            byte[] data = Base64.decode(fileContent, Base64.DEFAULT);
            try {
                text.append(new String(data, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        obj = new JSONObject(text.toString());
    }catch (Exception e){
        e.printStackTrace();
    }

我怎样才能读这种文件


共 (2) 个答案

  1. # 1 楼答案

    您正试图通过读取文件、迭代文件并将每一行附加到text对象,将整个文件读取到text对象中。您可以从text对象中创建JSONObject,该对象实际上只在最后一步中对应用程序有用

    在这里,当代码到达第obj = new JSONObject(text.toString());行时,您已经用输入文件的大小填充了堆,因为这个完整的文件以test对象的形式存在于内存中。然后对这个text对象进行JSONObject

    您可以采取以下措施来消除此问题:

    1. 使用BufferedReader分块读取文件(可选)。使用read()可能有点慢,最好有一个缓冲区
    2. 迭代文件并将条目以100010000的批处理方式放入text对象
    3. text中准备JSONObject并将其附加到obj
    4. 在处理下一批之前清除text对象,然后重复整个过程

    通过这样做,您只读取了内存中文件的一小部分,而且text对象充当缓冲区,只消耗了少量内存

    以下是示例代码段:

    int counter = 0;
    String temp = null;
    final int BATCH_SIZE = 1000;
    try (BufferedReader br = new BufferedReader(new FileReader(path)) {
    
        while ((temp = br.readLine()) != null) {
            text.append(temp);
            ++counter;
    
            /* Process In Batches */
            if(counter % BATCH_SIZE == 0) {
                /* Prepare & Append JSON Objects */
                obj = prepareAppendJSON(text.toString(), obj);
                /* Clear text */
                text.setLength(0);
            }
        }
    
        /* Last Iteration */
        obj = prepareAppendJSON(text, obj);
        text = new StringBuilder();
    
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    
  2. # 2 楼答案

    您唯一的选择是使用JSON streaming并对您感兴趣的事件作出反应

    import org.codehaus.jackson.*;
    
    .....
    
    JsonParser parser = new JsonFactory().createJsonParser( yourFileInputStream );
    parser.configure( Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true );
    parser.configure( Feature.ALLOW_SINGLE_QUOTES, true );
    // add more features
    
    for( JsonToken token = parser.nextToken(); null != token; token = parser.nextToken() ){
      switch( token ){
        case FIELD_NAME:
          doStuffWithName();
          break;
    
        case START_OBJECT:
          doObjectStart();
          break;
    
        case END_OBJECT:
          processObject();
          break;
    
        // other events
      }
    }
    

    我在4.0设备上使用了上述代码,JSON文件大小为10 MB

    另外,你需要先解码你原来的Base64文件。不确定是否可以在java.io.Stream内动态完成。在最坏的情况下,将Base64文件解压成一个简单的json,然后使用上面的json流代码