0

I have a Django application and form which accepts from a user an Excel(.xlsx) and CSV (.csv) file. I need to save both files to Azure Blob Storage. I found it to be trivial to handle the .csv file but the same code fails when attempting up upload an xlsx file:

from azure.storage.blob import BlobServiceClient

# This code executes successfully when saving a CSV to blob storage
blob_service_client = BlobServiceClient.from_connection_string(os.getenv('STORAGE_CONN_STRING'))
blob_client = blob_service_client.get_blob_client(container="my-container-name", blob=form.cleaned_data.get('name_of_form_field_for_csv_file'))
blob_client.upload_blob(form.cleaned_data.get('name_of_form_field_for_csv_file''))


# This code fails when saving xlsx to blob storage

blob_client = blob_service_client.get_blob_client(container="my-container-name", blob=form.cleaned_data.get('name_of_form_field_for_xlsx_file'))
blob_client.upload_blob(form.cleaned_data.get('name_of_form_field_for_xlsx_file''))

ClientAuthenticationError at /mypage/create/
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

However, I've been unable to figure out how to save the .xlsx file. I--perhaps somewhat naively--assumed I could pass the .xlsx file as-is (like the .csv example above) but I get the error:

ClientAuthenticationError at /mypage/create/
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

I found this SO Answer about the above error, but there's no concensus at all on what the error means and I've been unable to progress much further from that link. However, there was some discussion about sending the data to Azure blob storage as a byte stream. Is this a possible way forward? I should note here that, ideally, I need to process the files in memory as my app is deployed within App Service (my understanding is that I don't have access to a file system in which to create and manipulate files.)

I have also learned that .xlsx files are compressed so do I need to first decompress the file and then send it as a byte stream? If so, has anyone got any experience with this who could point me in the right direction?

Storage account connection string:

STORAGE_CONN_STRING=DefaultEndpointsProtocol=https;AccountName=REDACTED;AccountKey=REDACTED;EndpointSuffix=core.windows.net
Simon
  • 991
  • 8
  • 30
  • 1
    Please edit your question and include the connection string of your storage account. Please obfuscate the account name and key before sharing. – Gaurav Mantri Jul 11 '21 at 09:51
  • Added obfuscated storage account connection string as requested. – Simon Jul 11 '21 at 10:53
  • Interested to know what the storage account connection string will tell you. Are you able to elaborate more on this line of thinking? – Simon Jul 11 '21 at 11:03
  • In my experience authorization error happens in 2 scenarios - 1) Your account key is incorrect (Duh!, but it happens) and 2) The time on the machine where your code is running is off. Can you please check if both of them are correct. – Gaurav Mantri Jul 11 '21 at 13:57
  • I don't think it's an authorisation issue as the csv code executes successfully immediately before the xlsx code. I'd expect both to uploads to fail if it was an authorisation issue. I also checked the time - both are the same so I also don't think this is the issue either, sadly. – Simon Jul 11 '21 at 14:39
  • Is your file uploading properly? – Gaurav Mantri Jul 11 '21 at 14:41
  • Yep, no problems at all. I can download it directly from the portal, nothing corrupted. – Simon Jul 11 '21 at 15:03
  • 1
    Now I am confused :). Where in your code are you getting this error? Can you edit your question and provide that information. – Gaurav Mantri Jul 11 '21 at 15:04

2 Answers2

0

Did you try like below:

# Create a local directory to hold blob data
    local_path = "./data"
    os.mkdir(local_path)

# Create a file in the local data directory to upload and download
    local_file_name = str(uuid.uuid4()) + ".xlsx"
    upload_file_path = os.path.join(local_path, local_file_name)

# Write text to the file

    file = open(upload_file_path, 'w')
    file.write("Hello, World!")
    file.close()

# Create a blob client using the local file name as the name for the blob
    blob_client = 
    blob_service_client.get_blob_client(container=container_name, 
    blob=local_file_name)

# Upload the created file
with open(upload_file_path, "rb") as data:
    blob_client.upload_blob(data)

https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python

  • Thanks for the answer. I don't think I can leverage local temporary files as my deployment is within Azure App Service. I will edit my original question to say that I need to process the files in memory. – Simon Jul 11 '21 at 10:48
  • In that case, you have to create a temp file that will store in App service memory. – Kuldeep Singh Jul 11 '21 at 13:40
0

For reasons I don't fully understand (comments welcome for an explanation!), I can successfully save a .xlsx file to Azure Blob Storage with:

self.request.FILES['name_of_form_field_for_xlsx_file']

I suspect there's a difference in how csv vs. xlsx files are handled between request.FILES and form.cleaned_data.get() in Django, resulting in an authentication error as per the original question.

The full code to save a .csv and then a .xlsx is (note this is within a FormView):

from azure.storage.blob import BlobServiceClient

# Set connection string
blob_service_client = BlobServiceClient.from_connection_string(os.getenv('STORAGE_CONN_STRING'))

# Upload an xlsx file
blob_client = blob_service_client.get_blob_client(container="my-container", blob=self.request.FILES['xlsx_file'])
blob_client.upload_blob(self.request.FILES['xlsx_file'])

# Upload a CSV file
blob_client = blob_service_client.get_blob_client(container="my-container", blob=form.cleaned_data.get('csv_file'))
blob_client.upload_blob(form.cleaned_data.get('csv_file'))
Simon
  • 991
  • 8
  • 30