yoonvak
yoonvak

Reputation: 323

How to use "Azure storage blobs" for POST method in controller

I am creating an app where user can upload their text file and find out about its most used word.

I have tried to follow this doc to get used to the idea of using AZURE STORAGE BLOBS - https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet

But I am super newbie and having a hard time figuring it out how to adapt those blobs methods for my POST method.

This my sudo - what I think I need in my controller and what needs to happen when POST method is triggered.

a.No need for DELETE or PUT, not replacing the data nor deleting in this app

b.Maybe need a GET method, but as soon as POST method is triggered, it should pass the text context to the FE component

POST method

  1. connect with azure storage account
  2. if it is a first time of POST, create a container to store the text file a. how can I connect with the existing container if the new container has already been made? I found this, but this is for the old CloudBlobContainer. Not the new SDK 12 version.
.GetContainerReference($"{containerName}");
  1. upload the text file to the container
  2. get the chosen file's text content and return

And here is my controller.

public class HomeController : Controller
    {
        private IConfiguration _configuration;

        public HomeController(IConfiguration Configuration)
        {
            _configuration = Configuration;
        }

        public IActionResult Index()
        {
            return View();
        }

        [HttpPost("UploadText")]
        public async Task<IActionResult> Post(List<IFormFile> files)
        {
            if (files != null)
            {
                try
                {
                    string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");

                    BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);

                    string containerName = "textdata" + Guid.NewGuid().ToString();

                    BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);

                    //Q. How to write a if condition here so if the POST method has already triggered and container already created, just upload the data. Do not create a new container?

                    string fileName = //Q. how to get the chosen file name and replace with newly assignmed name?
                    string localFilePath = //Q. how to get the local file path so I can pass on to the FileStream?

                    BlobClient blobClient = containerClient.GetBlobClient(fileName);

                    using FileStream uploadFileStream = System.IO.File.OpenRead(localFilePath);
                    await blobClient.UploadAsync(uploadFileStream, true);
                    uploadFileStream.Close();

                    string data = System.IO.File.ReadAllText(localFilePath, Encoding.UTF8);

                    //Q. If I use fetch('Home').then... from FE component, will it receive this data? in which form will it receive? JSON?
                    return Content(data);
                }
                catch
                {
                    //Q. how to use storageExeption for the error messages
                }
                finally
                {
                    //Q. what is suitable to execute in finally? return the Content(data) here?
                    if (files != null)
                    {
                        //files.Close();
                    }
                }
            }
            //Q. what to pass on inside of the Ok() in this scenario?
            return Ok();


        }

    }

Q1. How can I check if the POST method has been already triggered, and created the Container? If so how can I get the container name and connect to it?

Q2. Should I give a new assigned name to the chosen file? How can I do so?

Q3. How can I get the chosen file's name so I can pass in order to process Q2?

Q4. How to get the local file path so I can pass on to the FileStream?

Q5. How to return the Content data and pass to the FE? by using fetch('Home').then... like this?

Q6. How can I use storageExeption for the error messages

Q7. What is suitable to execute in finally? return the Content(data) here?

Q8. What to pass on inside of the Ok() in this scenario?

Any help is welcomed! I know I asked a lot of Qs here. Thanks a lot!

Upvotes: 0

Views: 654

Answers (2)

Joshua Abbott
Joshua Abbott

Reputation: 442

@Ivan's answer is what the documentation seems the recommend; however, I was having a strange issue where my stream was always prematurely closed before the upload had time to complete. To anyone else who might run into this problem, going the BinaryData route helped me. Here's what that looks like:

await using var ms = new MemoryStream();
await file.CopyToAsync(ms);
var data = new BinaryData(ms.ToArray());
await blobClient.UploadAsync(data);

Upvotes: 0

Ivan Glasenberg
Ivan Glasenberg

Reputation: 29985

Update: add a sample code, you can modify it as per your need.

   [HttpPost]
    public async Task<IActionResult> SaveFile(List<IFormFile> files)
    {
        if (files == null || files.Count == 0) return Content("file not selected");

        string connectionString = "xxxxxxxx";
        BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);

        string containerName = "textdata" + Guid.NewGuid().ToString();;
        BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
        containerClient.CreateIfNotExists();

        foreach (var file in files)
        {
            //use this line of code to get file name
            string fileName = Path.GetFileName(file.FileName);
            BlobClient blobClient = containerClient.GetBlobClient(fileName);

            //directly read file content
            using (var stream = file.OpenReadStream())
            {
                await blobClient.UploadAsync(stream);
            }
        }

        //other code

        return View();
    }

Original answer:

When using List<IFormFile>, you should use foreach code block to iterate each file in the list.

Q2. Should I give a new assigned name to the chosen file? How can I do so?

If you want to keep the file original name, in the foreach statement like below:

                foreach (var file in myfiles)
                {
                    Path.GetFileName(file.FileName)

                     //other code
                 }

And if you want to assign a new file name when uploaded to blob storage, you should define the new name in this line of code: BlobClient blobClient = containerClient.GetBlobClient("the new file name").

Q3. How can I get the chosen file's name so I can pass in order to process Q2?

refer to Q2.

Q4. How to get the local file path so I can pass on to the FileStream?

You can use code like this: string localFilePath = file.FileName; to get the path, and then combine with the file name. But there is a better way, you can directly use this line of code Stream uploadFileStream = file.OpenReadStream().

Q5. How to return the Content data and pass to the FE? by using fetch('Home').then... like this?

Not clear what's it meaning. Can you provide more details?

Q6. How can I use storageExeption for the error messages

The storageExeption does not exist in the latest version, you should install the older one.

You can refer to this link for more details.

Upvotes: 1

Related Questions