Jacob Loncar
Jacob Loncar

Reputation: 109

Send a file that I want to make and store in memory as IHttpActionResult

I'm not sure what I want to do is even possible.

I'm currently working on an AngularJS web app and need implement a way to take data from my database, store it into a List<object>, parse that List<object> into a .csv file and load it in memory, that way I can send it back as HttpResponseMessage.

I already have all the data in a List<object> variable and will be using CSVHelper to parse that into a file, but all the examples I find online show me how to export the file locally with File.WriteAllText(), and they all use File.OpenRead(filePath) to read a file locally.

How can I create and store the file in memory and send it back as a HttpResponseMessage?


EDIT:

After digging around I was able to come up with this, although it doesn't work. When I run the code I get "This site can't be reached. The Connection was rest. ERR_CONNECTION_RESET" from chrome.

Hope this helps with visualizing what I'm trying to do.

The Javascript

me.downloadFile = function (fileId) {

    $http({
        method: 'POST',
        url: 'api/notices/CheckFileExists',
        params: { fileId: fileId }
    }).then(function success(result) {
        if (result.data === 'Y') {
            me.downloadOpen("POST", 'api/notices/DownloadFile?fileId=' + fileId, "", "");
        }
        else {
            $mdToast.show(
                $mdToast.simple()
                    .textContent(result.data)
                    .position("bottom right")
                    .hideDelay(2000)
            );
        }
    });
};

me.downloadOpen = function (verb, url, data, target) {
    var form = document.createElement("form");
    form.action = url;
    form.method = verb;
    form.target = target || "_self";

    if (data) {
        for (var key in data) {
            var input = document.createElement("input");
            input.name = key;
            input.value = typeof data[key] === "object" ? JSON.stringify(data[key]) : data[key];
            form.appendChild(input);
        }
    }

    form.style.display = 'none';
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);

The C#

[HttpPost]
    public HttpResponseMessage DownloadFile(int fileId)
    {
        var user = GetUser();
        var fileContent = NoticesMethods.GetNoticeFile(fileId, user);

        using (Globals.WebAppsImpersonator())
        {
            if (fileContent.Count > 0)
            {
                using (var mem = new MemoryStream())
                using (var writer = new StreamWriter(mem))
                using (var csvWriter = new CsvWriter(writer))
                {
                    csvWriter.Configuration.Delimiter = ",";
                    csvWriter.WriteField("File ID");
                    csvWriter.WriteField("Account");
                    csvWriter.WriteField("Move Date");

                    foreach (var row in fileContent)
                    {
                        csvWriter.WriteField("\"" + row.FileId + "\"");
                        csvWriter.WriteField("\"" + row.Account + "\"");
                        csvWriter.WriteField("\"" + row.MoveDate + "\"");
                    }

                    writer.Flush();

                    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
                    result.Content = new StreamContent(mem);
                    result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
                    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                    result.Content.Headers.ContentDisposition.FileName = "Notice.csv";

                    return result;
                }
            }
            return new HttpResponseMessage(HttpStatusCode.NotFound);
        }
    }

Upvotes: 0

Views: 2115

Answers (2)

Jacob Loncar
Jacob Loncar

Reputation: 109

I was able to find the answer by piecing together some stuff I found on the web. Incase anyone runs into a similar issue, this is the code that I have found to work for my case. The javascript is the same but I made some minor changes to the C#.

Instead of sending back StreamContent I send back StringContent. This way all I needed to do is write my data into a CSV file and then read it into a string.

Here is the code:

[HttpPost]
    public HttpResponseMessage DownloadFile(int fileId)
    {
        var user = GetUser();
        var fileContent = NoticesMethods.GetNoticeFile(fileId, user);


        using (Globals.WebAppsImpersonator())
        {
            if (fileContent.Count > 0)
            {
                using (var mem = new MemoryStream())
                using (var reader = new StreamReader(mem))
                using (var writer = new StreamWriter(mem))
                using (var csvWriter = new CsvWriter(writer))
                {
                    csvWriter.Configuration.Delimiter = ",";
                    csvWriter.WriteField("File ID");
                    csvWriter.WriteField("Account");
                    csvWriter.WriteField("Move Date");
                    csvWriter.NextRecord();

                    foreach (var row in fileContent)
                    {
                        csvWriter.WriteField("\"" + row.FileId + "\"");
                        csvWriter.WriteField("\"" + row.Account + "\"");
                        csvWriter.WriteField("\"" + row.MoveDate + "\"");
                        csvWriter.NextRecord();
                    }

                    writer.Flush();
                    mem.Position = 0;

                    var file = reader.ReadToEnd();

                    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
                    result.Content = new StringContent(file);
                    result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
                    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                    result.Content.Headers.ContentDisposition.FileName = "Notice.csv";

                    return result;
                }
            }
            return new HttpResponseMessage(HttpStatusCode.NotFound);
        }
    }

Upvotes: 0

William
William

Reputation: 107

To create a csv file and keep it in memory you'll need to instantiate a memory stream before your CSVHelper code. Then you can return the memory stream byte array as a base64 string from your controller.

[HttpGet]
public IHttpActionResult Get(params)
{
    var result = "";
    using (var memoryStream = new MemoryStream())
    {
        using (var streamWriter = new StreamWriter(memoryStream))
        using (var csvWriter = new CsvWriter(streamWriter))
        {
            csvWriter.WriteRecords(listOfObjects);
        } 

        result = Convert.ToBase64String(memoryStream.ToArray()); 
    }

    return Ok(result);
}

On the front end you'll probably want to take advantage of the FileSaver.js library. It requires you to convert the base64 string to a blob first. There are plenty of JS examples here (Creating a Blob from a base64 string in JavaScript).

Upvotes: 1

Related Questions