Retrieve Your Data¶
This guide shows how to fetch a previously uploaded file from your chosen Main Storage Provider (MSP) using its deterministic file key. You’ll use the file key to download the file and stream the bytes directly to disk—avoiding loading the whole object into memory.
Prerequisites¶
- Node.js v22+ installed
-
A TypeScript project
Need a starter project?
If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:
-
Create a new project folder by executing the following command in the terminal:
-
Initialize a
package.jsonfile using the correct command for your package manager: -
Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:
-
Create a
tsconfig.jsonfile in the root of your project and paste the following configuration: -
Initialize the
srcdirectory:
-
-
A file uploaded to DataHaven, along with the file key
Initialize the Script Entry Point¶
First, create an index.ts file, if you haven't already. Its run method will orchestrate all the logic in this guide, and you’ll replace the labelled placeholders with real code step by step. By now, your services folder (including the MSP and client helper services) should already be created. If not, see the Get Started guide.
The index.ts snippet below also imports fileOperations.ts, which is not in your project yet—that's expected, as you'll create it later in this guide.
Add the following code to your index.ts file:
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { downloadFile, verifyDownload } from './operations/fileOperations.js';
async function run() {
// For anything from @storagehub-sdk/core to work, initWasm() is required
// on top of the file
await initWasm();
const fileKeyHex = 'INSERT_FILE_KEY_AS_HEX';
// Convert to H256 type if not already
const fileKey = polkadotApi.createType('H256', fileKeyHex);
// Make sure the file extension matches the original file
const filePath = new URL(`./files/INSERT_FILENAME.png`, import.meta.url)
.pathname;
const downloadedFilePath = new URL(
'./files/INSERT_FILENAME_downloaded.png',
import.meta.url
).pathname;
// --- Retrieve file logic ---
// **PLACEHOLDER FOR STEP 1: ADD DOWNLOAD FILE HELPER METHOD**
// **PLACEHOLDER FOR STEP 2: VERIFY FILE HELPER METHOD**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Download and Save File¶
To download a file you've already uploaded to the network, you will create a downloadFile helper method through which you will retrieve the file from the network and then save it locally to your machine. Then, update the index.ts file accordingly in order to execute that logic.
Add Method to Download File¶
-
Create a new folder called
operationswithin thesrcfolder (at the same level as theservicesfolder) like so: -
Create a new file within the
operationsfolder calledfileOperations.ts. -
Add the following code:
fileOperations.tsimport { createWriteStream } from 'node:fs'; import { Readable } from 'node:stream'; import { H256 } from '@polkadot/types/interfaces'; import { mspClient } from '../services/mspService.js'; import { DownloadResult } from '@storagehub-sdk/msp-client'; export async function downloadFile( fileKey: H256, downloadPath: string ): Promise<{ path: string; size: number; mime?: string }> { // Download file from MSP const downloadResponse: DownloadResult = await mspClient.files.downloadFile( fileKey.toHex() ); // Check if the download response was successful if (downloadResponse.status !== 200) { throw new Error(`Download failed with status: ${downloadResponse.status}`); } // Save downloaded file // Create a writable stream to the target file path // This stream will receive binary data chunks and write them to disk. const writeStream = createWriteStream(downloadPath); // Convert the Web ReadableStream into a Node.js-readable stream const readableStream = Readable.fromWeb(downloadResponse.stream as any); // Pipe the readable (input) stream into the writable (output) stream // This transfers the file data chunk by chunk and closes the write stream automatically // when finished. return new Promise((resolve, reject) => { readableStream.pipe(writeStream); writeStream.on('finish', async () => { const { size } = await import('node:fs/promises').then((fs) => fs.stat(downloadPath) ); const mime = downloadResponse.contentType === null ? undefined : downloadResponse.contentType; resolve({ path: downloadPath, size, mime, // if available }); }); writeStream.on('error', reject); }); }
Call the Download File Helper Method¶
-
Proceed with updating the
index.tsfile with the following code in order to execute the download logic you just implemented:index.ts// Download file const downloadedFile = await downloadFile(fileKey, downloadedFilePath); console.log( `Downloaded ${downloadedFile.size} bytes to ${downloadedFile.path}` );View complete
index.tsfile up until this pointindex.tsimport '@storagehub/api-augment'; import { initWasm } from '@storagehub-sdk/core'; import { polkadotApi } from './services/clientService.js'; import { downloadFile, verifyDownload } from './operations/fileOperations.js'; async function run() { // For anything from @storagehub-sdk/core to work, initWasm() is required // on top of the file await initWasm(); const fileKeyHex = 'INSERT_FILE_KEY_AS_HEX'; // Convert to H256 type if not already const fileKey = polkadotApi.createType('H256', fileKeyHex); // Make sure the file extension matches the original file const filePath = new URL(`./files/INSERT_FILENAME.png`, import.meta.url) .pathname; const downloadedFilePath = new URL( './files/INSERT_FILENAME_downloaded.png', import.meta.url ).pathname; // Download file const downloadedFile = await downloadFile(fileKey, downloadedFilePath); console.log( `Downloaded ${downloadedFile.size} bytes to ${downloadedFile.path}` ); // Disconnect the Polkadot API at the very end await polkadotApi.disconnect(); } await run(); -
Run the script:
Upon a successful file download, you'll see output similar to:
ts-node index.ts Downloaded 18 bytes to /Users/username/Documents/dh-project/src/files/helloworld_downloaded.txt
Verify Downloaded File¶
To verify the integrity of the file you've just downloaded, you'll create a verifyDownload helper method through which the bytes of the original file will be matched to the bytes of the newly downloaded file. Then, you'll update the index.ts file accordingly in order to execute that logic.
Add Method to Verify Download¶
Implement the verifyDownload helper method logic to your fileOperations.ts file, by adding the following code:
// Compares an original file with a downloaded file byte-for-byte
export async function verifyDownload(
originalPath: string,
downloadedPath: string
): Promise<boolean> {
const originalBuffer = await import('node:fs/promises').then((fs) =>
fs.readFile(originalPath)
);
const downloadedBuffer = await import('node:fs/promises').then((fs) =>
fs.readFile(downloadedPath)
);
return originalBuffer.equals(downloadedBuffer);
}
View complete fileOperations.ts file
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { H256 } from '@polkadot/types/interfaces';
import { mspClient } from '../services/mspService.js';
import { DownloadResult } from '@storagehub-sdk/msp-client';
export async function downloadFile(
fileKey: H256,
downloadPath: string
): Promise<{ path: string; size: number; mime?: string }> {
// Download file from MSP
const downloadResponse: DownloadResult = await mspClient.files.downloadFile(
fileKey.toHex()
);
// Check if the download response was successful
if (downloadResponse.status !== 200) {
throw new Error(`Download failed with status: ${downloadResponse.status}`);
}
// Save downloaded file
// Create a writable stream to the target file path
// This stream will receive binary data chunks and write them to disk.
const writeStream = createWriteStream(downloadPath);
// Convert the Web ReadableStream into a Node.js-readable stream
const readableStream = Readable.fromWeb(downloadResponse.stream as any);
// Pipe the readable (input) stream into the writable (output) stream
// This transfers the file data chunk by chunk and closes the write stream automatically
// when finished.
return new Promise((resolve, reject) => {
readableStream.pipe(writeStream);
writeStream.on('finish', async () => {
const { size } = await import('node:fs/promises').then((fs) =>
fs.stat(downloadPath)
);
const mime =
downloadResponse.contentType === null
? undefined
: downloadResponse.contentType;
resolve({
path: downloadPath,
size,
mime, // if available
});
});
writeStream.on('error', reject);
});
}
// Compares an original file with a downloaded file byte-for-byte
export async function verifyDownload(
originalPath: string,
downloadedPath: string
): Promise<boolean> {
const originalBuffer = await import('node:fs/promises').then((fs) =>
fs.readFile(originalPath)
);
const downloadedBuffer = await import('node:fs/promises').then((fs) =>
fs.readFile(downloadedPath)
);
return originalBuffer.equals(downloadedBuffer);
}
Call the Verify Download Helper Method¶
-
Update the
index.tsfile with the following code in order to execute the verification logic you just implemented:index.tsconst isValid = await verifyDownload(filePath, downloadedFilePath); console.log(`File integrity verified: ${isValid ? 'PASSED' : 'FAILED'}`);View complete
index.tsfileindex.tsimport '@storagehub/api-augment'; import { initWasm } from '@storagehub-sdk/core'; import { polkadotApi } from './services/clientService.js'; import { downloadFile, verifyDownload } from './operations/fileOperations.js'; async function run() { // For anything from @storagehub-sdk/core to work, initWasm() is required // on top of the file await initWasm(); const fileKeyHex = 'INSERT_FILE_KEY_AS_HEX'; // Convert to H256 type if not already const fileKey = polkadotApi.createType('H256', fileKeyHex); // Make sure the file extension matches the original file const filePath = new URL(`./files/INSERT_FILENAME.png`, import.meta.url) .pathname; const downloadedFilePath = new URL( './files/INSERT_FILENAME_downloaded.png', import.meta.url ).pathname; // Download file const downloadedFile = await downloadFile(fileKey, downloadedFilePath); console.log( `Downloaded ${downloadedFile.size} bytes to ${downloadedFile.path}` ); const isValid = await verifyDownload(filePath, downloadedFilePath); console.log(`File integrity verified: ${isValid ? 'PASSED' : 'FAILED'}`); // Disconnect the Polkadot API at the very end await polkadotApi.disconnect(); } await run(); -
Run the script:
Upon a successful file download, you'll see the following output:
ts-node index.ts File integrity verified: PASSED
Next Steps¶
| Created: October 17, 2025