What's the most standard Java way to store raw binary data along with XML?

前端 未结 4 746
予麋鹿
予麋鹿 2020-12-31 21:13

I need to store a huge amount of binary data into a file, but I want also to read/write the header of that file in XML format.

Yes, I could just store the binary

4条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-31 22:06

    You can leverage AttachementMarshaller & AttachmentUnmarshaller for this. This is the bridge used by JAXB/JAX-WS to pass binary content as attachments. You can leverage this same mechanism to do what you want.

    • http://download.oracle.com/javase/6/docs/api/javax/xml/bind/attachment/package-summary.html

    PROOF OF CONCEPT

    Below is how it could be implemented. This should work with any JAXB impl (it works for me with EclipseLink JAXB (MOXy), and the reference implementation).

    Message Format

    [xml_length][xml][attach1_length][attach1]...[attachN_length][attachN]
    

    Root

    This is an object with multiple byte[] properties.

    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class Root {
    
        private byte[] foo;
        private byte[] bar;
    
        public byte[] getFoo() {
            return foo;
        }
    
        public void setFoo(byte[] foo) {
            this.foo = foo;
        }
    
        public byte[] getBar() {
            return bar;
        }
    
        public void setBar(byte[] bar) {
            this.bar = bar;
        }
    
    }
    

    Demo

    This class has is used to demonstrate how MessageWriter and MessageReader are used:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import javax.xml.bind.JAXBContext;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            root.setFoo("HELLO WORLD".getBytes());
            root.setBar("BAR".getBytes());
    
            MessageWriter writer = new MessageWriter(jc);
            FileOutputStream outStream = new FileOutputStream("file.xml");
            writer.write(root, outStream);
            outStream.close();
    
            MessageReader reader = new MessageReader(jc);
            FileInputStream inStream = new FileInputStream("file.xml");
            Root root2 = (Root) reader.read(inStream);
            inStream.close();
    
            System.out.println(new String(root2.getFoo()));
            System.out.println(new String(root2.getBar()));
        }
    
    }
    

    MessageWriter

    Is responsible for writing the message to the desired format:

    import java.io.ByteArrayOutputStream;
    import java.io.ObjectOutputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.activation.DataHandler;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.attachment.AttachmentMarshaller;
    
    public class MessageWriter {
    
        private JAXBContext jaxbContext;
    
        public MessageWriter(JAXBContext jaxbContext) {
            this.jaxbContext = jaxbContext;
        }
    
        /**
         * Write the message in the following format:
         * [xml_length][xml][attach1_length][attach1]...[attachN_length][attachN] 
         */
        public void write(Object object, OutputStream stream) {
            try {
                Marshaller marshaller = jaxbContext.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
                BinaryAttachmentMarshaller attachmentMarshaller = new BinaryAttachmentMarshaller();
                marshaller.setAttachmentMarshaller(attachmentMarshaller);
                ByteArrayOutputStream xmlStream = new ByteArrayOutputStream();
                marshaller.marshal(object, xmlStream);
                byte[] xml = xmlStream.toByteArray();
                xmlStream.close();
    
                ObjectOutputStream messageStream = new ObjectOutputStream(stream);
    
                messageStream.write(xml.length); //[xml_length]
                messageStream.write(xml); // [xml]
    
                for(Attachment attachment : attachmentMarshaller.getAttachments()) {
                    messageStream.write(attachment.getLength()); // [attachX_length]
                    messageStream.write(attachment.getData(), attachment.getOffset(), attachment.getLength());  // [attachX]
                }
    
                messageStream.flush();
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        private static class BinaryAttachmentMarshaller extends AttachmentMarshaller {
    
            private static final int THRESHOLD = 10;
    
            private List attachments = new ArrayList();
    
            public List getAttachments() {
                return attachments;
            }
    
            @Override
            public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
                return null;
            }
    
            @Override
            public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
                if(data.length < THRESHOLD) {
                    return null;
                }
                int id = attachments.size() + 1;
                attachments.add(new Attachment(data, offset, length));
                return "cid:" + String.valueOf(id);
            }
    
            @Override
            public String addSwaRefAttachment(DataHandler data) {
                return null;
            }
    
            @Override
            public boolean isXOPPackage() {
                return true;
            }
    
        }
    
        public static class Attachment {
    
            private byte[] data;
            private int offset;
            private int length;
    
            public Attachment(byte[] data, int offset, int length) {
                this.data = data;
                this.offset = offset;
                this.length = length;
            }
    
            public byte[] getData() {
                return data;
            }
    
            public int getOffset() {
                return offset;
            }
    
            public int getLength() {
                return length;
            }
    
        }
    
    }
    

    MessageReader

    Is responsible for reading the message:

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.ObjectInputStream;
    import java.io.OutputStream;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.activation.DataHandler;
    import javax.activation.DataSource;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.bind.attachment.AttachmentUnmarshaller;
    
    public class MessageReader {
    
        private JAXBContext jaxbContext;
    
        public MessageReader(JAXBContext jaxbContext) {
            this.jaxbContext = jaxbContext;
        }
    
        /**
         * Read the message from the following format:
         * [xml_length][xml][attach1_length][attach1]...[attachN_length][attachN] 
         */
        public Object read(InputStream stream) {
            try {
                ObjectInputStream inputStream = new ObjectInputStream(stream);
                int xmlLength = inputStream.read();  // [xml_length]
    
                byte[] xmlIn = new byte[xmlLength]; 
                inputStream.read(xmlIn);  // [xml]
    
                BinaryAttachmentUnmarshaller attachmentUnmarshaller = new BinaryAttachmentUnmarshaller();
                int id = 1;
                while(inputStream.available() > 0) {
                    int length = inputStream.read();  // [attachX_length]
                    byte[] data = new byte[length];  // [attachX]
                    inputStream.read(data);
                    attachmentUnmarshaller.getAttachments().put("cid:" + String.valueOf(id++), data);
                }
    
                Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
                unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);
                ByteArrayInputStream byteInputStream = new ByteArrayInputStream(xmlIn);
                Object object = unmarshaller.unmarshal(byteInputStream);
                byteInputStream.close();
                inputStream.close();
                return object;
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        private static class BinaryAttachmentUnmarshaller extends AttachmentUnmarshaller {
    
            private Map attachments = new HashMap();
    
            public Map getAttachments() {
                return attachments;
            }
    
            @Override
            public DataHandler getAttachmentAsDataHandler(String cid) {
                byte[] bytes = attachments.get(cid);
                return new DataHandler(new ByteArrayDataSource(bytes));
            }
    
            @Override
            public byte[] getAttachmentAsByteArray(String cid) {
                return attachments.get(cid);
            }
    
            @Override
            public boolean isXOPPackage() {
                return true;
            }
    
        }
    
        private static class ByteArrayDataSource implements DataSource {
    
            private byte[] bytes;
    
            public ByteArrayDataSource(byte[] bytes) {
                this.bytes = bytes;
            }
    
            public String getContentType() {
                return  "application/octet-stream";
            }
    
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(bytes);
            }
    
            public String getName() {
                return null;
            }
    
            public OutputStream getOutputStream() throws IOException {
                return null;
            }
    
        }
    
    }
    

    For More Information

    • http://bdoughan.blogspot.com/2011/03/jaxb-web-services-and-binary-data.html

提交回复
热议问题