javaaemjcr

How to write an nt:file programmatically


I'm trying to store binary data in JCR, which is created on the fly. My problem is that the only way provided by the JCR API is via an InputStream:

Session session = request.getResourceResolver().adaptTo(Session.class);
ValueFactory valueFactory = session.getValueFactory();
Binary bin = valueFactory.createBinary(is);

As CQ/Sling is RESTful I can see why this is the case as you usually get a form post or an httprequest to another source, where you always have an InputStream to use. But in my case I am creating the binary on the fly which usually is represented as an OutputStream.

  1. Is there any other way I overlooked in the JCR API where I could create an OutputStream directly on the nt:file node, just like a FileOutputStream?
  2. If no, is there an easy way to have an OutpuStream transformed to an InputStream?

I know the other way is available from the Apache Commons IOUtils.copy(). I've seen some examples on SO where they just use the ByteArrayOutputStream.toByteArray() to create an InputStream. But as the data could get rather large, this is not a good solution. Besides I tried it and somehow the stream was incomplete, so it seems there is a buffer limmit. The next approach was with piped streams, but there I have other problems to which I opened another question: Multiple quotes cause PipedOutputStream/OutputStreamWriter to fail

EDIT: Removed PipedStream code example as I posted the issue with it in another question. So here I am still just looking for an easy way to create an nt:file where the input is not an InputStream.


Solution

  • Pipes are good solution here. However, in order to implement them properly, you have to use two threads: first should write data into the PipedOutputStream and the second should create a Binary from PipedInputStream and save it into JCR:

    final PipedInputStream pis = new PipedInputStream();
    final PipedOutputStream pos = new PipedOutputStream(pis);
    Executors.newSingleThreadExecutor().submit(new Runnable() {         
        @Override
        public void run() {
            try {
                OutputStreamWriter writer = new OutputStreamWriter(pos);
                writer.append("append here some data");
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    Binary binary = session.getValueFactory().createBinary(pis);
    session.getNode("/content/myNode").setProperty("xyz", binary);
    session.save();
    

    The symmetrical solution, in which you handle the JCR in the new thread would be also good.