有 Java 编程相关的问题?

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

java Un/将base64编码的二进制数据编组为流

我有一个应用程序,使用JAXB从Oracle数据库向XML导入和导出数据。现在数据库中有一些BLOB字段,其中包含上传的文件,我希望这些文件在XML中以base64编码字符串的形式存在。这在使用@XmlSchemaType(name = "base64Binary")的JAXB中非常适合,如下所示:

@XmlType
public class DocumentTemplateFile {

    // other fields ommited

    @XmlElement(required = true)
    @XmlSchemaType(name = "base64Binary")
    private byte[] data;

    // other code ommited
}

此解决方案的问题是,由于字节数组,整个文件内容都存储在内存中。根据文件的大小,这可能会导致一些问题

因此,我想知道是否有一种方法可以创建一个XmlAdapter或类似的文件,从文件中获取流,这样我就可以直接从数据库的BLOB中获取流,而不必将整个内容存储在内存中。我在想类似的事情:

public class BlobXmlAdapter extends XmlAdapter<InputStream, OutputStream> {

  @Override
  public InputStream marshal(final OutputStream value) throws Exception {
    return null;
  }

  @Override
  public OutputStream unmarshal(final InputStream value) throws Exception {
    return null;
  }

}

这显然只是一个说明性的例子,这样你就可以知道我在找什么。最终的解决方案不一定要使用XmlAdater。我所需要的只是一种方法来钩住取消/编组过程,并通过缓冲区/队列流式传输数据,而不是将所有内容存储在字节数组中


共 (2) 个答案

  1. # 1 楼答案

    创建自定义XmlAdapter,如下所示:

    public class Base64FileAdapter extends XmlAdapter<String, File>{
        @Override
        public String marshal(File file) throws Exception {
            // todo: read file and convert to base64 and return
        }
    
        @Override
        public File unmarshal(String data) throws Exception {
            File file = File.createTempFile("dataFile", "binary");
            file.deleteOnExit();
            //todo: base64 decode string data and write bytes to file
            return file;
        }
    }
    

    现在在你的bean中,使用它:

    @XmlElement(required = true)
    @XmlJavaTypeAdapter(Base64FileAdapter.class)
    private File dataFile;
    

    现在,整个二进制内容存储在文件中。您可以读取/写入此文件。这个文件在jvm退出时被删除

  2. # 2 楼答案

    此解决方案使用以下第三方库。您应该使用以下maven依赖项:

    <dependency>
        <groupId>jlibs</groupId>
        <artifactId>jlibs-xsd</artifactId>
        <version>2.0</version>
    </dependency>
    
    <repository>
        <id>jlibs-snapshots-repository</id>
        <name>JLibs Snapshots Repository</name>
        <url>https://raw.githubusercontent.com/santhosh-tekuri/maven-repository/master</url>
        <layout>default</layout>
    </repository>
    

    我们需要使用以下自定义XmlAdapter

    import javax.xml.bind.annotation.adapters.XmlAdapter;
    import java.io.File;
    
    /**
     * @author Santhosh Kumar Tekuri
     */
    public class Base64Adapter extends XmlAdapter<String, File>{
        @Override
        public File unmarshal(String v) throws Exception{
            return new File(v);
        }
    
        @Override
        public String marshal(File v) throws Exception{
            throw new UnsupportedOperationException();
        }
    }
    

    现在将pojo更改为使用上述适配器:

    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlSchemaType;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    import java.io.File;
    
    @XmlRootElement
    public class DocumentTemplateFile {
        @XmlElement(required = true)
        public String userName;
    
        @XmlElement(required = true)
        @XmlSchemaType(name = "base64Binary")
        @XmlJavaTypeAdapter(Base64Adapter.class)
        public File data;
    }
    

    现在,应该使用以下帮助器类来读取xml文件:

    import jlibs.xml.Namespaces;
    import jlibs.xml.xsd.DOMLSInputList;
    import jlibs.xml.xsd.XSParser;
    import jlibs.xml.xsd.XSUtil;
    import org.apache.xerces.xs.XSElementDeclaration;
    import org.apache.xerces.xs.XSModel;
    import org.apache.xerces.xs.XSSimpleTypeDefinition;
    import org.apache.xerces.xs.XSTypeDefinition;
    import org.xml.sax.Attributes;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.XMLReader;
    import org.xml.sax.helpers.XMLFilterImpl;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.SchemaOutputResolver;
    import javax.xml.namespace.QName;
    import javax.xml.parsers.SAXParserFactory;
    import javax.xml.transform.Result;
    import javax.xml.transform.sax.SAXSource;
    import javax.xml.transform.stream.StreamResult;
    import java.io.*;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author Santhosh Kumar Tekuri
     */
    public class JAXBBlobUtil{
        public static XSModel generateSchemas(Class clazz) throws Exception{
            final Map<String, ByteArrayOutputStream> schemas = new HashMap<String, ByteArrayOutputStream>();
            JAXBContext.newInstance(clazz).generateSchema(new SchemaOutputResolver(){
                @Override
                public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException{
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    schemas.put(suggestedFileName, bout);
                    StreamResult result = new StreamResult(bout);
                    result.setSystemId(suggestedFileName);
                    return result;
                }
            });
    
            DOMLSInputList lsInputList = new DOMLSInputList();
            for(Map.Entry<String, ByteArrayOutputStream> entry : schemas.entrySet()){
                ByteArrayInputStream bin = new ByteArrayInputStream(entry.getValue().toByteArray());
                lsInputList.addStream(bin, entry.getKey(), null);
            }
            return new XSParser().parse(lsInputList);
        }
    
        private static Object unmarshal(Class clazz, InputSource is) throws Exception{
            XSModel xsModel = generateSchemas(clazz);
            JAXBContext context = JAXBContext.newInstance(clazz);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            xmlReader = new Base64Filter(xmlReader, xsModel);
            return context.createUnmarshaller().unmarshal(new SAXSource(xmlReader, is));
        }
    
        private static class Base64Filter extends XMLFilterImpl{
            private XSModel schema;
            private List<QName> xpath = new ArrayList();
            private FileWriter fileWriter;
            public Base64Filter(XMLReader parent, XSModel schema){
                super(parent);
                this.schema = schema;
            }
    
            @Override
            public void startDocument() throws SAXException{
                xpath.clear();
                super.startDocument();
            }
    
            @Override
            public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
                super.startElement(uri, localName, qName, atts);
    
                xpath.add(new QName(uri, localName));
                XSElementDeclaration elem = XSUtil.findElementDeclaration(schema, this.xpath);
                if(elem!=null){
                    XSTypeDefinition type = elem.getTypeDefinition();
                    if(type.getTypeCategory()==XSTypeDefinition.SIMPLE_TYPE){
                        XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition)type;
                        while(!Namespaces.URI_XSD.equals(simpleType.getNamespace()))
                            simpleType = (XSSimpleTypeDefinition)simpleType.getBaseType();
                        if("base64Binary".equals(simpleType.getName())){
                            try{
                                File file = File.createTempFile("data", "binary");
                                file.deleteOnExit();
                                fileWriter = new FileWriter(file);
                                String absolutePath = file.getAbsolutePath();
                                super.characters(absolutePath.toCharArray(), 0, absolutePath.length());
                            }catch(IOException ex){
                                throw new SAXException(ex);
                            }
                        }
                    }
                }
            }
    
            @Override
            public void characters(char[] ch, int start, int length) throws SAXException{
                try{
                    if(fileWriter==null)
                        super.characters(ch, start, length);
                    else
                        fileWriter.write(ch, start, length);
                }catch(IOException ex){
                    throw new SAXException(ex);
                }
            }
    
            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException{
                xpath.remove(xpath.size() - 1);
                try{
                    if(fileWriter!=null)
                        fileWriter.close();
                    fileWriter = null;
                }catch(IOException ex){
                    throw new SAXException(ex);
                }
                super.endElement(uri, localName, qName);
            }
        };
    }
    

    现在读取xml文件,如下所示:

    public static void main(String[] args) throws Exception{
        DocumentTemplateFile obj = (DocumentTemplateFile)unmarshal(DocumentTemplateFile.class, new InputSource("sample.xml"));
        // obj.data refers to File which contains base64 encoded data
    }