在文件JSON编辑中,不加载文件的所有数据

时间:2017-06-19 11:46:24

标签: android json

目前我正在为Android设备开发web服务,设备可以将json数据上传到服务器(tomcat8上的java webapp),然后将数据保存到db(mysql)中。

我的问题是,数据包含图像,每个上传文件可能增长到大约100 MB。图像不得与其他信息分开上传。

所以我需要将所有数据放在一个json中并上传它。

我想知道是否有可能使用某些json库访问json文件而不完全加载它并直接向文件中添加更多数据。

给出这个类的例子:

public class Person {

    private String name;
    private String surname;
    private Address address;
    private List<ImageData> data;

    public class Address {
        private String address;
        private String city;
        private String state;
    }

    public class ImageData {
        private String id;
        private String base64;
    }
}

人员生成如下:

public static String toJson(Person person) {
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("name", person.getName());
            jsonObject.put("surename", person.getSurname());

            JSONObject jsonAdd = new JSONObject();
            jsonAdd.put("address", person.getAddress().getAddress());
            jsonAdd.put("city", person.getAddress().getCity());
            jsonAdd.put("state", person.getAddress().getState());

            jsonObject.put("address", jsonAdd);

            JSONArray jsonArray = new JSONArray();

            for(Person.ImageData pn : person.getImageData()) {
                JSONObject jsonPhone = new JSONObject();
                jsonPhone.put("id", pn.getId());
                jsonPhone.put("base64", pn.getBase64());
                jsonArray.put(jsonPhone);
            }

            jsonObject.put("imageData", jsonArray);

            return jsonObject.toString();

        } catch (JSONException e) {
            e.printStackTrace();
        }

        return null;
    }

输出如下:

{
        "imageData": [
        {
            "base64": "someimage", 
                "id": "11111"
        }
   ],
        "address": {
        "state": "somewhere",
                "address": "somewhere, 000",
                "city": "somewhere city"
    },
        "surname": "sure",
            "name": "last"
    }

对于填充了所有字段的现有Person和一个Imagedata我想添加一个额外的ImageData而不加载ImageData类型的整个JsonArray,因此输出将是:

{
        "imageData": [
        {
            "base64": "someimage", 
                "id": "11111"
        },
        {
            "base64": "someimage",
                "id": "2222"
        }
   ],
        "address": {
        "state": "somewhere",
                "address": "somewhere, 000",
                "city": "somewhere city"
    },
        "surname": "sure",
            "name": "last"
    }

是否可以在不加载所有内容的情况下直接编辑文件?就像搜索imagedata容器但没有加载内容(id和base64)然后只是添加一个额外的元素(id,base 64)到容器?

1 个答案:

答案 0 :(得分:0)

在尝试编辑json时,我得到了另一个想法,而不是创建一个大文件进行编辑。

首先,我保存没有图片的数据,只将文件路径放到图像而不是图片中。

因为我只需要JSON上传到网络服务,所以我可以在获取所有正确信息时生成它。

因此,我准备了一个JSON Builder来创建大型文件,该文件使用Jackson依赖于Jackson库将给定数据流式传输到新文件中:

dependencies {
...
    compile 'com.fasterxml.jackson.core:jackson-core:2.7.2'
...
}

这个进口:

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;

在方法buildLargeJson(File[], Person)中,我首先在public_storage/documents中创建一个新文件,然后创建一个JsonGenerator,用于通过新文件的FileStream传输数据。

public static void buildLargeJson(File[] files, Person p) {
        JsonFactory factory = new JsonFactory();
        File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
        File file = new File(dir, "large.json");

        try {
            if (!dir.exists())
                dir.mkdirs();

            if (!file.exists())
                file.createNewFile();

            JsonGenerator generator = factory.createGenerator(new FileWriter(file));
            generator.writeStartObject();
            generator.writeFieldName("name");
            generator.writeString(p.getName());
            generator.writeFieldName("surname");
            generator.writeString(p.getSurname());

            if (files != null && files.length > 0) {
                generator.writeFieldName("imageData");
                generator.writeStartArray();
                int counter = 1;
                for (File f : files) {
                    generator.writeStartObject();
                    generator.writeFieldName("id");
                    generator.writeNumber(counter++);
                    generator.writeFieldName("base64");
                    byte[] b = byteArrayFromFile(f);

                    if (b != null) {
                        generator.writeBinary(b);
                    } else {
                        generator.writeString("image not found");
                    }

                    b = null;

                    generator.writeEndObject();
                }
                generator.writeEndArray();
            }
            generator.writeEndObject();
            generator.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

使用我在问题中输入的Person类并将所有图片添加到此人。使用通过byteArrayFromFile(File)方法生成的图像本身的字节数组,图片当然保存为Base64。通过读取图像的FileInputStream而不加载位图来节省内存来创建字节数组。即使对于大小约为25MB的图片,应用程序仅使用少于40MB的RAM来生成120MB的JSON文件。字节到Base64的转换由writeBinary(byte[])的杰克逊库方法JsonGenerator完成。

private static byte[] byteArrayFromFile(File imageFile) {
        FileInputStream fis = null;
        byte[] buffer = new byte[8192];
        byte[] b = null;
        int bytesRead;

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            fis = new FileInputStream(imageFile);
            while ((bytesRead = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }

            b = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }    
        return b;
    }

使用Android API 19和25标准WXGA平板电脑仿真器和三星A3 2016(Nougat 7.0)进行测试。