I am trying to implement an upload to Azure Storage. I am unable to use @azure/storage-blob
package as it conflicts with every version of Node, including versions 16, 18, 20, etc. It always throws the error node:os module not found
. So, I switched to a custom solution, but I am facing a major issue with the following error:
Error uploading file to Azure Blob Storage: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Please note the following checks I have already done:
Content-Type
and x-ms-date
headers. My machttps://accountName.blob.core.windows.net/containerName/folder/subfolder/parking.jpg
.const fetch = require('node-fetch');
const crypto = require('crypto');
const { sanitize, getMediasBucketName } = require('../../utils');
const logger = require('../../utils/logger');
class AzureBlobStorage {
constructor() {
this.accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
this.accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY;
this.containerName = process.env.AZURE_CONTAINER_NAME;
if (!this.accountName || !this.accountKey || !this.containerName) {
throw new Error('Azure Storage account name, key, or container name is missing.');
}
// Set the endpoint URL for the Blob Storage container
this.endpoint = `https://${this.accountName}.blob.core.windows.net/${this.containerName}`;
}
_generateAuthorizationHeader(method, path, headers) {
const canonicalHeaders = Object.keys(headers)
.filter(key => key.toLowerCase().startsWith('x-ms-'))
.sort()
.map(key => `${key.toLowerCase()}:${headers[key]}`)
.join('\n');
const stringToSign = [
method,
'',
headers['Content-Type'] || '',
headers['x-ms-date'],
canonicalHeaders,
`/${this.accountName}/${this.containerName}${path}`
].join('\n');
// Generate the signature using HMAC-SHA256
const decodedKey = Buffer.from(this.accountKey, 'base64');
const signature = crypto.createHmac('sha256', decodedKey).update(stringToSign).digest('base64');
const authorizationHeader = `SharedKey ${this.accountName}:${signature}`;
return authorizationHeader;
}
async storeFileInDirectory({
projectName,
fileName,
contentBody,
contentType,
mediaType,
}) {
try {
const safeFileName = sanitize(fileName);
const filePath = `/${projectName}/${mediaType}/${safeFileName}`;
const url = `${this.endpoint}${filePath}`;
const headers = {
'x-ms-date': new Date().toUTCString(),
'Content-Type': contentType,
'Content-Length': contentBody.length,
'x-ms-blob-type': 'BlockBlob',
};
headers['Authorization'] = this._generateAuthorizationHeader('PUT', filePath, headers);
const response = await fetch(url, {
method: 'PUT',
headers: headers,
body: contentBody,
});
if (!response.ok) {
throw new Error(`Error uploading file to Azure Blob Storage: ${response.statusText}`);
}
logger.info(`File uploaded to Azure: ${filePath}`);
return {
filePath
};
} catch (e) {
logger.error('Error uploading file to Azure Blob Storage', {
errorMessage: e.message,
stack: e.stack,
params: { projectName, fileName, mediaType },
});
return null;
}
}
}
Error uploading file to Azure Blob Storage: Server failed to authenticate the request.
You can use the below code which will upload the file with path in the Azure Blob Storage
using JavaScript.
Code:
const fetch = require('node-fetch-commonjs');
const CryptoJS = require('crypto-js');
class AzureBlobStorage {
constructor() {
this.accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
this.accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY;
this.containerName = process.env.AZURE_CONTAINER_NAME;
if (!this.accountName || !this.accountKey || !this.containerName) {
throw new Error('Azure Storage account name, key, or container name is missing.');
}
// Set the endpoint URL for the Blob Storage container
this.endpoint = `https://${this.accountName}.blob.core.windows.net/${this.containerName}`;
}
_generateAuthorizationHeader(method, path, headers) {
// Generate canonical headers (x-ms- headers sorted alphabetically)
const canonicalHeaders = Object.keys(headers)
.filter((key) => key.toLowerCase().startsWith('x-ms-'))
.sort()
.map((key) => `${key.toLowerCase()}:${headers[key]}`)
.join('\n');
// Generate canonical resource
const canonicalResource = `/${this.accountName}/${this.containerName}${path}`;
// Construct string to sign
const stringToSign = [
method, // HTTP method
headers['Content-Encoding'] || '', // Content-Encoding
headers['Content-Language'] || '', // Content-Language
headers['Content-Length'] || '', // Content-Length
headers['Content-MD5'] || '', // Content-MD5
headers['Content-Type'] || '', // Content-Type
'', // Date (not used with x-ms-date)
headers['If-Modified-Since'] || '',
headers['If-Match'] || '',
headers['If-None-Match'] || '',
headers['If-Unmodified-Since'] || '',
headers['Range'] || '',
canonicalHeaders, // Canonicalized headers
canonicalResource // Canonicalized resource
].join('\n');
// Sign the string with the account key
const decodedKey = CryptoJS.enc.Base64.parse(this.accountKey);
const signature = CryptoJS.HmacSHA256(stringToSign, decodedKey).toString(CryptoJS.enc.Base64);
// Return the authorization header
return `SharedKey ${this.accountName}:${signature}`;
}
async storeFileInDirectory({ projectName, fileName, contentBody, contentType, mediaType }) {
try {
// Sanitize file name to avoid invalid characters
const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, '_');
// Construct file path and full URL
const filePath = `/${projectName}/${mediaType}/${safeFileName}`;
const url = `${this.endpoint}${filePath}`;
// Headers required for the PUT request
const headers = {
'x-ms-date': new Date().toUTCString(),
'Content-Type': contentType,
'Content-Length': Buffer.byteLength(contentBody),
'x-ms-blob-type': 'BlockBlob',
'x-ms-version': '2020-04-08', // Use a valid API version
};
// Add the Authorization header
headers['Authorization'] = this._generateAuthorizationHeader('PUT', filePath, headers);
// Make the PUT request to Azure Blob Storage
const response = await fetch(url, {
method: 'PUT',
headers: headers,
body: contentBody,
});
if (!response.ok) {
throw new Error(`Error uploading file to Azure Blob Storage: ${response.status} - ${response.statusText}`);
}
console.log(`File uploaded successfully to Azure Blob Storage: ${filePath}`);
return { filePath };
} catch (e) {
console.error('Error uploading file to Azure Blob Storage', {
errorMessage: e.message,
stack: e.stack,
params: { projectName, fileName, mediaType },
});
return null;
}
}
}
// Example usage
(async () => {
const azureStorage = new AzureBlobStorage();
const result = await azureStorage.storeFileInDirectory({
projectName: 'exampleProject',
fileName: 'example.txt',
contentBody: 'This is an example file content.', // Replace with actual file content as a string
contentType: 'text/plain', // Replace with actual MIME type
mediaType: 'images',
});
console.log(result);
})();
Output:
PS C:\> node example.js
File uploaded successfully to Azure Blob Storage: /exampleProject/images/example.txt
{ filePath: '/exampleProject/images/example.txt'
Portal: