9
votes

I'm currently having issue with Azure File storage when I build up a URL with a shared access signature (SAS) Token. The file will download in the browser, but the content-type is always application/octet-stream rather than changing to match the mime type of the file. If I put the file in Azure BLOB storage and build up a URL with a SAS Token, it sends the correct content-type for my file (image/jpeg).

I've upgraded my storage account from V1 to V2 thinking that was the problem, but it didn't fix it.

Does anyone have a clue what I could try that might get Azure File storage to return the correct content-type using a URL with SAS Token to download the file?

7
What is the value of content type property for that file.Gaurav Mantri
I just tried the same with a file in one of my storage accounts (in file storage). The content type of file was set as image/png. I was able to see the file properly and the response headers contain proper content-type header value.Gaurav Mantri
I've upgraded my storage account from V1 to V2 thinking that was the problem, but it didn't fix it. - Off topic comment: Please be aware of the costs of V2 storage accounts. They are more expensive than V1 accounts.Gaurav Mantri
@GauravMantri How do you tell without downloading it? I don't see anything in the portal that identifies content-type. Properties shows Name, URL, Last modified, size, etag and content-md5. I uploaded it with the portal and was able to determine content-type only by getting it with postman. The content type of my jpg comes back as application/octet-stream every time. Thanks for pricing tip as well.David Yates
Sorry, I misunderstood your question. For some reason I thought that even though you're setting the content type correctly, the service is returning application/octet-stream. You would need to make a separate REST API call to File Service to fetch a file's properties. Not all properties for a file are returned as part of listing operation. You can use Microsoft's Storage Explorer to see a file's properties.Gaurav Mantri

7 Answers

4
votes

When I upload a jpeg file to file share through portal, content-type is changed to application/octet-stream indeed. But I can't reproduce your download problem.

I didn't specify content-type in my SAS request uri, but the file just download as a jpeg file. Have tested in SDK(Account SAS/Stored Access Policy/SAS on file itself) or REST API, both work even without content-type.

You can try to specify the content-type using the code below.

 SharedAccessFileHeaders header = new SharedAccessFileHeaders()
 {
     ContentDisposition = "attachment",
     ContentType = "image/jpeg"
 };
string sasToken = file.GetSharedAccessSignature(sharedPolicy,header);
4
votes

Azure blob falls to the default value of 'application/octet-stream' if nothing is provided. To get the correct mimetypes, this is what I did with my flask app:

@app.route('/', methods=['GET', 'POST'])
def upload_file():
        if request.method == 'POST':
            f = request.files['file']
            mime_type = f.content_type
            print (mime_type)
            print (type(f))
            try:
                blob_service.create_blob_from_stream(container, f.filename, f,
                content_settings=ContentSettings(content_type=mime_type))
            except Exception as e:
                print (str(e))
                pass

mime_type was passed to ContentSettings to get the current mimetypes of files uploaded to azure blob.

In nodeJS:

blobService.createBlockBlobFromStream(container, blob, stream, streamLength, { contentSettings: { contentType: fileMimeType } }, callback)

where:

fileMimeType is the type of the file being uploaded

callback is your callback implementation

Reference to method used: https://docs.microsoft.com/en-us/javascript/api/azure-storage/azurestorage.services.blob.blobservice.blobservice?view=azure-node-latest#createblockblobfromstream-string--string--stream-readable--number--createblockblobrequestoptions--errororresult-blobresult--

1
votes

So far these are the only fixes for the content-type that I've found:

  1. Use the Microsoft Azure Storage Explorer to modify the content-type string by hand. You have to right click the file and the left-click properties to get the dialog to appear.
  2. Programmatically modify the file using Microsoft's WindowsAzure.Storage Nuget package.
  3. Surface file download via my own web site and not allow direct access.

For me, none of these are acceptable choices. The first two can lead to mistakes down the road if a user uploads a file via the portal or Microsoft Azure Storage Explore and forgets to change the content type. I also don't want to write Azure Functions or web jobs to monitor and fix this problem.

Since blob storage does NOT have the same problems when uploading via Microsoft Azure Storage Explore or via the portal, the cost is much lower AND both work with SAS Tokens, we are moving towards blob storage instead. We do lose the ability to mount the drive to our local computers and use something like Beyond Compare to do file comparisons, but that is a disadvantage that we can live with.

If anyone has a better solution than the ones mentioned above that fixes this problem, I will gladly up-vote it. However, I think that Microsoft will have to make changes for this problem to be fixed.

0
votes
// here you define your file content type
CloudBlockBlob cloudBlockBlob = container.GetBlockBlobReference(file.FileName);
                cloudBlockBlob.Properties.ContentType = file.ContentType; //content type
0
votes

This works with java using com.microsoft.azure azure-storage library. Uploading to Shared Access Signature resource.

        InputStream is = new FileInputStream(file);
        CloudBlockBlob cloudBlockBlob = new CloudBlockBlob(new URI(sasUri));
        cloudBlockBlob.getProperties().setContentType("application/pdf");
        cloudBlockBlob.upload(is, file.length());
        is.close();

enter image description here

0
votes

For anyone looking to upload files correctly with a declared Content Type, the v12 client has changed setting Content type. You can use the ShareFileHttpHeaders parameter of file.Create

ShareFileClient file = directory.GetFileClient(fileName);          
using FileStream stream = File.OpenRead(@"C:\Temp\Amanita_muscaria.jpg");           
file.Create(stream.Length, new ShareFileHttpHeaders { ContentType = ContentType(fileName) });           
file.UploadRange(new HttpRange(0, stream.Length),stream);

where ContentType(fileName) is a evaluation of filename, eg:

if (fileName.EndsWith(".txt")) return "text/plain";
// etc
0
votes

I know that I'm not answering the question, but I do believe the answer is applicable. I had the same problem with a storage account that I need it to have it as a static website. Whenever I upload a blob to a container, the default type is "application/octet-stream" and because of this the index.html get downloaded instead of being displayed.

To change the file type do the following:

# Get Storage Account for its context
$storageAccount = Get-AzStorageAccount -ResourceGroupName <Resource Group Name> -Name <Storage Account Name>
# Get Blobs inside container of storage account
$blobs = Get-AzStorageBlob -Context $storageAccount.Context -Container <Container Name>
foreach ($blob in $blobs) {
    $CloudBlockBlob = [Microsoft.Azure.Storage.Blob.CloudBlockBlob] $blob.ICloudBlob
    $CloudBlockBlob.Properties.ContentType = <Desired type as string>
    $CloudBlockBlob.SetProperties()
}

Note: for Azure File storage you might wanna change the library to [Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob]