Upload Media Files

Overview on how to configure a client for media files uploads

Configuring a client to access the file storage service from the NxtFi API requires specific knowledge. Since the operation needs to be securely recorded in the blockchain without compromising computational power, certain actions must be taken on the client side to achieve this objective. The steps described below guarantee a fully transparent and efficient mechanism for uploading files up to 10 MB in size each.

Is good to remember that the client will need:

  1. Obtain the necessary client credentials: Acquire the required credentials, such as authorized key-pair from the NxtFi API to authenticate the client.

  2. Establish a secure connection: Configure the client to establish a secure connection with the NxtFi API endpoint using HTTPS.

  3. Prepare the file for upload: Prior to uploading the file, ensure that it is properly prepared. This may involve validating the file format, checking for any restrictions on file types, and compressing the file if necessary. (At the moment any type is accepted up to 10MB for each call)

Steps to upload file

1 - Request Upload URL

The first step after checking content-type and size is to request a pre signed url to upload the file. A JWT signature is going to be required in order to confirm that who signed the request and upload the file to the temporary staging area is in possession of authorized keys with write permissions in NxtFi API

  • Generate a SHA-256 Hash from the file.

  • Build the JWT signature using the JOSE library as described below:

  by = by.toLowerCase(); // the entity owner of the private key.
  const alg = "RS256";
  const pkcs8priv = pemPriv; //example: "-----BEGIN RSA PRIVATE KEY-----MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMWdzSQfk5eznBFCb0mCYVwpBSaKx7UW0K1Mx3TJ479wdJ0eLGOYqdBnDuS7xs7d+PYhaDxKA/IIAOxcVkRHq4/m26RQ4btbr6OY7zS5pc7CYPcTwJW2b8a4gca1rjsw7Ziv4qDTp8LwawvlVcfap1vfAvjGAIdbOOC6rSoSn8pVtJqyAVbzBY3ggGfY7fP62cDMfBV6Hp/ZH3OPfR5vhZd1G/WcBrtWMOk5p7kgvAVpF60ysS9xspKxgq7Uw3nsruwa2zMIpIeXlFBUmAEp6hiwsNwInoMNQ+jpJPKPWhLo3vvCH9ZAWVZ0yZbGgSDC80I56m3RjYHwVkvNeg4SUNAgMBAAECggEARKSqyLb1orRvAczOZLCJZ/kZxwRk34dqknKTcgGqHl/qU6Nwi0yXS8dbsmCeTpRk0+bAZj/jtBw8JX161lhbwWDG3+RoSwst4LYIAVxHqgzhbIoQN+9ZRjl9f5DOCjGIIMPHHWAM67HATu84Jp1bomx8LXU1fs26PM3eBVhHhcXMl929eoWe+BIKtUir5qqYhPF8Oq1eLW2qO8i+9vhicwUeHn3MFZjR8Ty2F4mJenaN90Ek/1tpvLM8Cu8j/Yfj0heBuHhBdXTC4LCNqvfZj+MdK/L7XOxaE+UvV80jHgzTalxKBRHyNzzeBAoSagCkLQFX1Td3kznisQXLuJFiTQKBgQDAyJZoboKxnFr/f/DqUiKOFkFbA2+i8zoZPmqeDtGiwfbugVUBVRAG5eRq1LAUtAXlNC9DN9NqgzjxmAmVa9MN/6myivjg3tZNifP4j/dq8dO7D6BnRUkkhRXuTodKxZ8ScdjaTRqOHKrGBDcd5Qb94NgQ8SrpoKphW1Tm4DOFXwKBgQC6X8X/PEJ6CujwNAj2oOq/w9Hie0SQVTFCq/zJqLNC7GmPc8ET8dwQqNW4PVs+eUtbuxo8VgldaMBhEUtwWO5ZkhfUsWgJyIjR3jKwpbB6eaoHrhCdUF0wE/OH/Cezur5H+b/AeD57ix0DCpzuMxkHx/ATCVTDzAelXP/qL0MhEwKBgBPxrH3JUQQG5PMhzU6wiJqieshrppT5DL2n02feqJlp753lC2JD5pCQH/1dW4oIxcNcjrcpg1m1kaKM1BD9QqxkEq5B6lV5ODp1VyQT4MjTk8/6YeHNLS/2BLrOrPhzUW2bEZAkAedJ1/D8ZqdVFlAVfsTh7kqVMIs546Ku9yWJAoGAY75R9tFHOo2QMM2IZoWkXNDuCOmzvhL59BabiUlR9uUTcYSfto7gGcJh7uJXbM35eLRfzB63kFg5bTmWSCAwH5vCSUBQz7uVDcx/EG78Te/DAa00kxypYsuqsAJRNS0iHN8asmUn+3JuKmyJpCmQocttPqLlzIvrI6LsC2cT5CUCgYBLuWHHn0Mf+Vn5jdncAdh5FWj3K/IlVLDCUAXp+dJ4IGgd2QxxwHqz4HT1/VHdp9nERFiK7lXD9yYEfYFtrsc3DGF9Ic3J8FWf0DNbnBc8mIgBtEXeYOMdJTuIiOH+3c/pMuWhbMI6gmpKcR0y6W5lQUg0qBExphG8YabXHga/OA==-----END RSA PRIVATE KEY-----"
  
  const privateKey = await jose.importPKCS8(pkcs8priv, alg);

  const jwt = await new jose.SignJWT({ by: by, fileHash: fileHash, fileSize: sizeInBytes })
      .setProtectedHeader({ alg })
      .setIssuedAt()
      .setExpirationTime("24h") 
      .sign(privateKey);

Files reaching staging area will have a life-cycle until complete deletion of 24hrs. Avoid changing the setExpirationTime() property to prevent uncaught exceptions.

Send the PUT request to /newfile endpoint.

(See Presign URL request for call details)

2 - Upload File to staging area

After receiving the success response from the PUT call to /newfile, the content can be uploaded to the provided URL using the response fields as template call fields. In this context, we will describe the implementation of the POST method using the FormData browser API.

//create a formData

 const formData = new FormData();
    formData.append("acl", "public-read"); //condition outside response to add
    Object.entries(response.fields).forEach(([k, v]) => {
      formData.append(k, v);
    });
//append file as the last element of the formDaata to avoid uncaught exceptions.
    formData.append("file", fileContent);

Perform POST call

// POST req to received url
fetch(response.url, {
      method: "POST",
      body: formData,
    })
    .then((res) => {
        if (res.ok) console.log("FILE successfully loaded");
        }
        // res code status 204 indicates succesfull upload

Once a file is successfully uploaded, it will be temporarily stored in a staging folder while waiting for persistence. This staging folder serves as an intermediate storage location before the file is permanently stored. However, files in the staging folder have a maximum duration of 24 hours.

To ensure the file's long-term persistence, a block must be signed by the same entity who uploaded the file. By propagating a blockchain-confirmed block, its storage and availability are guaranteed.

If the file is not persisted within the 24-hour timeframe, it will be permanently deleted from the staging folder. This mechanism ensures that only files persisted in blocks are permanently stored.

3 - Send a new block to persist the file

Use the custom function persistArchive to ensure file long-term and proper path generation. A optional data field is available to add custom metadata to the file block registration

persistArchive({ 
    name: "filename.pdf", //include file extension
    fileHash: "file-hash-string", // the fileHash used to sign JWT
    data: {YOUR-CUSTOM-DATA},  // optional property, metadata field.
});

Smart Contract Result

Treated as every stored value in the NxtFi storage system, the file can be required through a get function at contract level:

const responseObj = get({name: "filename"}); 

or as an endpoint query call:

https://test-001-node.cloud.nxtfi.org/v2/_storage/<scope>/filename

Note: When querying the result value, neither at contract level or at endpoint call the extension of the file is provided.

and the response object will be like this below. Will include the Url were the file is located and can be downloaded.

{
  "scope": "scope",
  "name": "filename.pdf",
  "data": {
    "Nombre": "ING. Elvis Bonilla",
    "Cedula": "V-123434124"
  },
  "blockHash": "b42547cb3e698cbd1574901148551e28a3085de1c594323ecf455eb38df0e830",
  "fileHash": "73d5a33577951cfbc16638f7f6500847db10d42bba1dae9267cb126e16060e6c",
  "url": "https://nxtfi-bucket-us-east-1.s3.amazonaws.com/blockchain/v2/test-001/_archive/__<scope>/<fileHash>.<extention>"
}

Last updated