WebAPI - zwrócenie CSV bez tworzenia pliku po stronie serwera

0

Hej,

mniej więcej mam funkcji w WebAPI który ma za zadanie zwrócić plik CSV:

 HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
              response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "Visits.csv";

            return response;

Mam teraz takie pytanie czy jest możliwość stworzenia tego pliku jakoś w pamięci i z niej pobranie go do respone.Content? Bo obecnie na początku tej funkcji tworze ten plik na pulpicie i nie wiem czy to zadziała później :/

Pytanie motywuje tym że pliki które mi zwraca ten serwis są puste i wydaje mi sie ze nie ma dostępu do pulpitu u mnie na kompie.

0

Użyj MemoryStream zamiast FileStream.

0

Tak mam teraz:

        public HttpResponseMessage ExportVisits()
        {
            string pathDesktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            string filePath = pathDesktop + "\\Visits.csv";

            if (!File.Exists(filePath))
            {
                File.Create(filePath).Close();
            }
            string delimter = ",";

           
            byte[] bytes = null;
            try
            {
                using(MemoryStream ms = new MemoryStream())
                using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                {
                    bytes = new byte[(file.Length)];
                    file.Read(bytes, 0, (int) file.Length);
                    ms.Write(bytes, 0, (int)file.Length);                  
                }
            }
            catch (Exception e)
            {
               //return false;
            }
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content =  new StreamContent(new MemoryStream(bytes));
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
            return response;         
        }

I jak uzyje fiddlera to niespecjalnie to dziala, nic widze w nim tego pliku ani jakis danych, zgadza sie np. rozmiar wysylanych danych.

0

To nie jest kod o który pytałem. Skąd biorą się dane w pliku Visits.csv?
Po prostu zamiast pisać dane do pliku na dysku przez FileStream zapisz do MemoryStream, a potem zwróć ten MemoryStream w response.Content.

0

z repozytorium dostaje liste obiektow, na jej podstawie używając filestream pisze do pliku, tworzać CSV, który potem chce zwrócić

0

No więc zapisz tę listę do MemoryStream zamiast pliku i tyle. :)

0

W ten sposób:

 try
            {
                using (MemoryStream writer = new MemoryStream())
                {
                    bytes = new byte[(file.Length)];
                    file.Read(bytes, 0, (int) file.Length);
                    writer.Write(bytes, 0, (int) file.Length);
                    foreach (var li in list)
                    {
                        var t = GetBytes(li.AverageTimeBetween + ", " + li.How );
                        writer.Write(t, 0, t.Length);
                    }
                    writer.Close();
                }
            }
            catch (Exception e)
            {
               //return false;
            }
            file.Close();

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content =  new StreamContent(new MemoryStream(bytes));
              response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
            
            return response;

?
też nie działa i nie wiem co to jest co zwraca mi takie zapytanie (patrze w fidler)

Takie coś mam jako zawartość tego pliku:

{"version":{"major":1	minor:1	build:-1	revision:-1	majorRevision:-1	minorRevision:-1}	content:{"headers":[{"key":"Content-Type"	value:["text/csv"]}	{"key":"Content-Disposition"	value:["attachment; filename=Visits.csv"]}]}	statusCode:200	reasonPhrase:"OK"	headers:[]	requestMessage:null	isSuccessStatusCode:true}

Ktoś coś? :(

0

Nie używaj żadnego file!!!
Użyj jednego MemoryStreama, do którego zapiszesz swoje dane (tak jak do każdego innego Streama w .NET), a potem go zwrócisz w HttpResponseMessage.

Przepraszam, nie umiem prościej tego wytłumaczyć.

0

No to jak mam zadeklarować tą tablicę bytes ? muszę mieć jej rozmiar jakoś :/
mam listę obiektów "list", w każdym obiekcie są jakieś dane. chce z tego zrobić CSV i to zwrócić...
EDIT:

            var list = repository.MakeACall();
            
            byte[] bytes = null;
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
                     
                using (MemoryStream writer = new MemoryStream())
                {
                    foreach (var li in list)
                    {
                        var t = GetBytes(li.AverageTimeBetween + ", " + li.How);
                        writer.Write(t, 0, t.Length);
                    }             
                    response.Content = new ByteArrayContent(writer.ToArray());
                }           
          
         
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
            var r = Request.CreateResponse(HttpStatusCode.OK, bytes);
            r.Content.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
            return response;

co robię źle?
Wiem że dane są w tej liście, jest około 15 000 rekordów. Jak popatrze w fiddler to mi przychodzi Body o wielkości 310 przykładowo... :/

1

A, czyli nie umiesz korzystać ze strumieni. Generalnie strumienie są oparte o taki fajny wzorzec Dekorator, dzięki czemu możemy opakować strumień - medium transmisyjne (plik - FileStream, sieć - NetworkStream, pamięć - MemoryStream) w strumień pozwalający na wygodne czytanie i pisanie (np. StreamWriter, StreamReader).

Wielkiej filozofii w tym nie ma:

using (var ms = new MemoryStream())
{
    using (var sw = new StreamWriter(ms))
    {
        foreach (var visit in list)
        {
            string line = string.Join(",", visit.AverageTimeBetween, visit.How);
            sw.WriteLine(line);
            sw.Flush();
        }

        ms.Position = 0;
                    
        response.Content =  new StreamContent(ms);
    }
}
0

Dalej jest ten sam problem że w tym pliku nic nie ma praktycznie oprócz tego co wcześniej napisałem :/ tak jakby w ogóle nie dodawał tego do Content

1

A w MemoryStreamie coś jest?

0

Ogólnie wydaje mi się że nie ma tych wartości z tym streamie co podałeś.

1

Chodziło mi bardziej o to, co zwraca wywołanie ms.ToArray()?
A tak w ogóle, to pokaż całą tę metodę jak ona wygląda teraz, zamiast wklejać screenshoty. ;)

0
 public class Test
    {
        public string Test1 { get; set; }
        public string Test2 { get; set; }
    }

      [System.Web.Http.HttpGet]
        [Route("api/analytics/export")]
        public HttpResponseMessage ExportVisits()
        {
            var list2 = new List<Test>
            {
                new Test {Test1 = "asd", Test2 = "2"},
                new Test {Test1 = "asdsdfdas", Test2 = "3"},
                new Test {Test1 = "asd23dsf", Test2 = "4"},
                new Test {Test1 = "asd23423", Test2 = "5"},
                new Test {Test1 = "asd5423", Test2 = "6"}
            };

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
       
                using (var ms = new MemoryStream())
                {
                   using (var sw = new StreamWriter(ms))
                    {
                        foreach (var analyticsRow in list2)
                        {
                            string line = string.Join(",", analyticsRow.Test1 ?? string.Empty,
                                analyticsRow.Test2 ?? string.Empty);
                            sw.WriteLine(line);
                             sw.Flush();
                        }
                        ms.Position = 0;
                        var t  = new StreamContent(ms);
                        response.Content = t;
                    }

                }

                response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/CSV");
                response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
                response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
            return response;
        }

Jak ustawie ms.ToArray() to wtedy w tym jest byte[55] i jakieś numerki w każdym polu tablicy.

A tak w widoku to odbieram:

  $http.get('./api/analytics/export',  { responseType: 'arraybuffer' })
  .success(function (data) {
      var file = new Blob([data], { type: 'application/csv' });
      saveAs(file, 'Visits.csv');

                console.log(file);
      var reader = new FileReader();
     
      reader.readAsArrayBuffer(file);
  });

W tej mojej przykładowej tablicy mam chyba ileś elementów. Fiddler pokazal mi ze Body ma 337 (chyba znaków? ) więc dodałem jeszcze więcej tych rekordów do listy i rozmiar się nie zmienił :/
Czyli do response w ogóle się to nie dodaje :/

0

Okay temat zakończony. Zrobiłem to za pomocą tego narzędzia: http://epplus.codeplex.com/
Bardzo łatwo i fajnie. Jedyny problem jaki mam to po kliknięciu dwa razy na ściągnięty plik Excel się pyta czy otworzyć bo plik wygląda na "corrupt", ale to się da naprawić tylko muszę znaleźć jak.

1

@ne0, dlaczego te linijki:

response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/CSV");
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
return response;

umieściłeś poza sekcją using? Poza nią sekcja MemoryStream jest usunięty, więc zapewne response.Content jest pusty i stąd wynika Twój problem.
Naprawdę nie potrzebujesz do tego oddzielnej biblioteki.

0

Takie coś:

 public HttpResponseMessage ExportVisits1()
        {
            var list2 = new List<Test>
            {
                new Test {Test1 = "asd", Test2 = "2"},
                new Test {Test1 = "asdsdfdas", Test2 = "3"},
                new Test {Test1 = "asd23dsf", Test2 = "4"},
                new Test {Test1 = "asd23423", Test2 = "5"},
                new Test {Test1 = "asd5423", Test2 = "6"}
            };

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);

            using (var ms = new MemoryStream())
            {
                using (var sw = new StreamWriter(ms))
                {
                    foreach (var analyticsRow in list2)
                    {
                        string line = string.Join(",", analyticsRow.Test1 ?? string.Empty,
                            analyticsRow.Test2 ?? string.Empty);
                        sw.WriteLine(line);
                        sw.Flush();
                    }
                    ms.Position = 0;
                    var t = new StreamContent(ms);
                    response.Content = t;
                    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/CSV");
                    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
                    response.Content.Headers.ContentDisposition.FileName = "Visits.csv";
           
                }
               }
            return response;
        }

zwraca mi dokładnie to samo co wcześniej.

1

Bo popełniasz dokładnie ten sam błąd. return response też musi być w using, w przeciwnym wypadku MemoryStream jest usunięty, więc i Content także.

0
using (var ms = new MemoryStream())
            {
                using (var sw = new StreamWriter(ms))
                {
                    foreach (var analyticsRow in list2)
                    {
                        string line = string.Join(",", analyticsRow.Test1 ?? string.Empty,
                            analyticsRow.Test2 ?? string.Empty);
                        sw.WriteLine(line);
                        sw.Flush();
                    }
                    ms.Position = 0;
                    var t = new StreamContent(ms);
                    response.Content = t;
                    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
                    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
                    response.Content.Headers.ContentDisposition.FileName = "Visits.xls";
                    return response;
                }
                
            }

tak mam w widoku:

            $http.get('./api/analytics/export1',  { responseType: 'arraybuffer' })
  .success(function (data) {
      var file = new Blob([data], { type: 'application/vnd.ms-excel' });
      saveAs(file, 'Visits.xls');
      var reader = new FileReader();
      reader.readAsArrayBuffer(file);
  });           

No nie działa...

1

Ale nie działa Ci po stronie serwera czy klienta?

0

Kodowanie pobierania?
chyba chodzi Ci o to:

   $http.get('./api/analytics/export1',  { responseType: 'arraybuffer' })
  .success(function (data) {
      var file = new Blob([data], { type: 'application/vnd.ms-excel' });
      saveAs(file, 'Visits.xls');
      var reader = new FileReader();
      reader.readAsArrayBuffer(file);
  });         
1

A tak próbowałeś:

using (var sw = new StreamWriter(ms, Encoding.UTF8))
var file = new Blob([data], { encoding: 'UTF-8', type: 'application/vnd.ms-excel' });
/*lub*/ 
var file = new Blob(["\ufeff", data], {  type: 'application/vnd.ms-excel' });
0

Dobra, trochę szkoda bo mnie irytują takie sytuacje jak się nie udaje i nawet nie wiem dokładnie czemu :/ :/
Problem uważam za rozwiązany poprzez wykorzystanie tej biblioteki EPPlus. Natomiast jeśli chcecie możemy dalej próbować :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1