mintBlue
GuidesTransactions

Store a file

Publish a file's bytes with its name and content type, optionally signed and encrypted, then recover the file from the record.

A file output publishes the file itself, so the record carries the document rather than a pointer to a document that somebody has to keep hosting.

What a file output takes

The shape as @mintblue/sdk 9.6.0 declares it, verbatim:

export interface FileOutput {
    type: 'file';
    /** File to publish */
    value: {
        fileName: string;
        contentType: string;
        content: Buffer;
    };
    /** Set to true to add digital signature */
    sign?: boolean;
    /** Set to true to encrypt value */
    encrypt?: boolean;
}

Three things about value are worth stating plainly, because each one is a way the call goes wrong:

  • All three fields are required. fileName is not optional. A value with only contentType and content does not satisfy the declared type.
  • content is a Buffer. Reading a file with an encoding argument, as in fs.readFileSync(path, 'base64'), returns a string, not a buffer. Read it without an encoding.
  • contentType is a MIME type, the same string a web server would send: application/pdf, image/png, text/plain. The list is at MDN.

The maximum file size is 50MB per transaction.

Read the file and submit it

const fs = require('node:fs');
const path = require('node:path');

const filePath = path.join(__dirname, 'invoice.pdf');

const outputs = [
  {
    type: 'file',
    value: {
      fileName: path.basename(filePath),
      contentType: 'application/pdf',
      content: fs.readFileSync(filePath),
    },
    sign: true,
    encrypt: true,
  },
];

const { txid } = await client.createTransaction({
  project_id: process.env.MINTBLUE_PROJECT_ID,
  outputs,
});

sign and encrypt behave as they do for a data output: the signature is made with your key, and the encryption happens on your machine before anything is submitted. Both default to off.

Check it worked

Fetch the transaction with parse: true, find the file output, and write the bytes back to disk. If the recovered file opens and matches the original, the whole path worked.

const transaction = await client.getTransaction({ txid, parse: true });

const file = transaction.outputs.find((output) => output.type === 'file');
fs.writeFileSync(`recovered-${file.value.fileName}`, file.value.content);

A stricter check, if the file matters: compare digests rather than eyeballing the result.

const { utils } = require('@mintblue/sdk');

const original = await utils.sha256s(fs.readFileSync(filePath));
const recovered = await utils.sha256s(file.value.content);
console.log(original === recovered ? 'identical' : 'different');

When a digest is enough

Publishing the whole file is not always what you want. If the point is to be able to prove later that a document has not been altered, the hash output type publishes a digest of the file and leaves the file with you. It takes the raw contents as data and an algorithm, either SHA-256 or SHA-512, and computes the digest for you. The format it writes is documented at hash.

Next

On this page