Uploading Attachement on Corda Ledger

Home Blockchain Uploading Attachement on Corda Ledger

R3 Corda, an open source blockchain platform. As we know blockchains are the future. All of the platforms are going to be submerged in blockchain only. So, we are the coders, the developers have to learn as well as adjust with this too.

While creating one solution, we, here at Webmob Software Solutions Pvt. Ltd. found a challenge to upload and download the attachments on and from Corda Ledger. After bit of research, our team of experts found this solution connecting scattered pieces across internet. I thought of sharing it so that it can help others on the quest.

I generally use Kotlin for the development in R3 Corda and recently made a code for uploading the documents and then downloading the documents through hash as well as with name of the document. So, lets start with that only, let me show you the code first, and then I’ll explain how I did it. Uploading the zipped and normal files.

@PostMapping(value = ["uploadFile"], produces =[APPLICATION_JSON_VALUE])
 fun uploadFile(@RequestParam file: MultipartFile, @RequestParam uploader: String):ResponseEntity<String>{
     val filename = file.originalFilename
     val uploader = uploader
     require(filename != null) {"File Name must be set"}
     val hash: SecureHash = if(!(file.contentType == "zip" || file.contentType == "jar")){
             upload(file.inputStream, uploader,filename!!)
                 jar = file.inputStream,
                 uploader = uploader,
                 filename = filename!!


     return ResponseEntity.created(URI.create("attachments/$hash")).body("Attachment uploaded with hash - $hash")

private  fun upload(inputStream: InputStream, uploader: String,filename:String): AttachmentId{
     val zipName = "$filename-${UUID.randomUUID()}.zip"
    FileOutputStream(zipName).use { fileOutputStream ->
        ZipOutputStream(fileOutputStream).use { zipOutputStream ->
            val zipEntry = ZipEntry(filename)
            inputStream.copyTo(zipOutputStream, 1024)
    return FileInputStream(zipName).use { fileInputStream ->
        val hash = proxy.uploadAttachmentWithMetadata(
                jar = fileInputStream,
                uploader = uploader,
                filename = filename
So, as we all know the main property of any block in any blockchain is it’s Hash. That is how a block is known and that is how a block’s uniqueness is defined. So, all of my code also revolvesaround the hash.Now, in the first line I’ve declared the request type that is POST, the name of request is “Upload request” and the response type that is expected is a JSON file. After that I have made two functions, the two functions are uploadfile and uploadzip respectively. There in the first function the syntax is pretty simple, it goes like, The name of the function is uploadfile and it has two arguements. The first arguement is the filename i.e. which file is to uploaded, and the second arguement is the name or id of the uploader, it can be shifted according to the demand.

After that, entering the function i have declared two variables, the first is made for the name of the original file so that it can be looked up into the docs once it is uploaded with the same name. Second is for the name or ID of the uploader as you choose it. Here, the filename should not be null, it is made sure using the check there. After these two another variable is defined, that is for the hash and there I have used the secure hash class’s container which is used for generating a cryptographic hash using different algorithms, however only SHA-256 is supported at the moment. Now, if you’ll pay atttention there, I’ve used the if condition there, so if the the file type is found to be zip, or jar then uploadzip function is called, otherwise the file is uploaded with the proxy.uploadAttachmentWithMetadata function.

After that the function returns the ResponseEntity with the hash that has been generated for the file. Now, what if the file was found to be a jar or zipped file, then the “uploadZip” function is called and it works like, here uploadZip fiunction has mainly three arguements which were passed during the function call. Now when the function gets the zipped file, first of all it unzips it and then again zips it back also uploading it as an attachment to the metadata simultaneously. Now these were the functions that I made for uploading the documents.

Downloading using the Hash
@GetMapping(value = ["downloadFile/{hash}"], produces = [APPLICATION_JSON_VALUE])
fun downloadFileByHash(@PathVariable hash:String):ResponseEntity<Resource>{
    val inputStream = InputStreamResource(proxy.openAttachment(SecureHash.parse(hash)))
    return ResponseEntity.ok().header(
            "attachment; filename=\"$hash.zip\""
Now, here in this function I am downloading the file using the Hash that was generated in the prior function. Here the first Getmapping tells the system that it is a get request whoes name is “download/{hash}” and gives a response in JSON format. Getting into the function an arguement is passsed to the function which is the Hash of the file you want to download. This will help to open the InputStream and access the SecureHash container with the hash of the file.

Now, once the stream is open and assigned to the inputStream variable, the function returns the ResponseEntity with the headers alotted with hash value i.e. how the file was stored in the Stream. The body of this JSON response contains the data from the inputStream where the data was stored with the uploading function. Downloading with the Name
@GetMapping(value = ["downloadFileByName"], produces = [APPLICATION_JSON_VALUE])
fun downloadFileByName(@RequestParam name:String):ResponseEntity<Resource>{
    val attachmentIds: List<AttachmentId> = proxy.queryAttachments(
            AttachmentQueryCriteria.AttachmentsQueryCriteria(filenameCondition = Builder.equal(name)),
    val inputStreams = attachmentIds.map { proxy.openAttachment(it) }
    val zipToReturn = inputStreams.single()
    return ResponseEntity.ok().header(
            "attachment; filename=\"$name.zip\""
Now, when we need to download the file with the name, which can always be the case, as names are much easier to remember than the Hash, and it is not that meaningful to remember the Hash, when you can perform the task with the help of the name. So, here again the GetMapping shows the get request with the name downloadFileByName and expects a JSON response.


The function takes the name of the file as an arguement, wich further goes as the arguement for the query which fetches the attachmentID from the MetaData, and assigns it to the variable. Next inputStreams variable recieves the attachment as it is mapped and then opened using the proxy class. After that the file is again zipped and assigned to the variable. After all of these commands are i ver we now have the file and just need it to be returned to the user. So, the function returns the file from the stream using the name.zip and puts the zipped file into the body of the response JSON.

Conclusion If you have any prior experience in coding and with kotlin, this is quite easy of an task once you get to know the basic functions and methods in R3 Corda. Many of these can be found in the Corda documentations here https://docs.corda.net/. Still I’ve tried to explain and made these codes as easy as possible for the beginners as well as the developers who are trying to enter the field. Got any queries, feel free to bug me up.



Comments (2)

  1. Avatar
    October 26, 2019
    bitcoin revolution this morning

    Bitcoin wallets store and use this digital

  2. Avatar
    November 13, 2019

    Full Review of Cryptopia Cryptocurrency Exchange.

Leave A Comment

Your email address will not be published. Required fields are marked *