This post is dedicated to all the people I have seen in the Corda Slack channel asking how to upload and subsequently download attachments to and from a node. Although you can up- and download attachments via the Corda Shell, I will focus on writing a client application that interacts with a node to manage the attachments.

Hopefully, this short post will help you, the reader, to implement this feature independently of any help from us.

As a Spring Fanboy, I will obviously be implementing the client application in Spring. By leveraging spring-web we can put together an application with little effort, letting Spring do the heavy lifting. What does this mean for you? Well, it allows you to send and receive files to and from the Spring application by adding a few annotations and using the right objects. Praise be to Spring! 🙌🙏

One last thing, I am sure it is possible to write better code that achieves the same outcome as the code in this post. If you write that, you can always submit a Pull Request to change the example in Github 😎. The post can be found here if you need it.

Let’s get on with it.

Uploading

As I mentioned earlier, I’m sure that much can be done to improve this example, but hey, it does the job.

I don’t think there is too much to dive into here, since a lot of the code is around checking and possibly converting the file into the right (ZIP or JAR) format.

Question: Why is the format conversion needed in the first place? Well, only ZIPs or JARs can be stored as attachments in Corda. Therefore, if a different file type is received, it must first be converted so it can be stored on the node.

The only Corda-specific code here is the call to uploadAttachmentWithMetadata . (Setting the file name becomes handy when attempting to retrieve and download the stored attachment.)

The hash ( AttachmentId , an alias for SecureHash ) is returned from the endpoint. As with the filename , this provides another way to retrieve the attachment later on.

Example: Uploading the attachment:

Downloading

I have implemented two different versions for downloading attachments.

By Hash / AttachmentId

This endpoint retrieves an attachment using the hash returned from the upload endpoint. openAttachment requires a hash to pull the attachment from the node.

The hash is passed in as a String . Note: This is because there doesn’t appear to be any default conversion to SecureHash set up by Spring, and, in my humble opinion, it is not worth the effort to set this up for this situation.

The correct header needs to be added. This header triggers the download of the attachment by the caller. Calling this endpoint returns a result like this:

By name

This snippet, like the uploading example, has a lot of extra code that plays around with any OutputStream and InputStream .

This endpoint makes use of the queryAttachments function to retrieve any AttachmentId that matches the input criteria. Important: A call to openAttachment is still needed to return the attachment once the AttachmentId s or hashes are retrieved.

Going down this route makes much more sense than the downloadByHash endpoint, because a filename is more human-readable than a hash.

Most of the extra code in this example handles the possibility of multiple files being returned. If there are multiple attachments with the same name, they get zipped together and returned.

You can then make a request like the following, which can handle many attachments with the same name:

Note that the file size here is larger than the ‘by hash’ version. This is because I uploaded another file with the same name.

Wrapping up

There isn’t really a proper conclusion to write here. All I have to say is that, if you want to upload and download attachments to and from a Corda node with Spring, then the code in this post will help to get you on your way. I am sure there are tidier ways to handle all of the InputStream s and OutputStream s, but these seem to get the job done.

If you found this post helpful and want to keep up with my posts as I write them, then you can follow me on Twitter at @LankyDanDev.