0

I am struggling with being able to create a file with its data based on the byte array returned from the WebAPI. The following is my code for making the call to the web api

 using (var http = new WebClient())
            {
                string url = string.Format("{0}api/FileUpload/FileServe?FileID=" + fileID, webApiUrl);
                http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
                http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
                http.DownloadDataCompleted += Http_DownloadDataCompleted;
                byte[] json = await http.DownloadDataTaskAsync(url);



            }

The api code is

[HttpGet]
    [Route("FileServe")]
    [Authorize(Roles = "Admin,SuperAdmin,Contractor")]
    public async Task<HttpResponseMessage> GetFile(int FileID)
    {
        using (var repo = new MBHDocRepository())
        {
            var file = await repo.GetSpecificFile(FileID);
            if (file == null)
            {
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }

            var stream = File.Open(file.PathLocator, FileMode.Open);
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new StreamContent(stream);
            response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
            return response;
        }
    }

I receive a byte array as a response however am unable to create the corresponding file from that byte array. I have no idea how to convert the byte array into the relevant file type (such as jpg, or pdf based on file type in the web api). any help will be appreciated.

Miriam Farber
  • 18,986
  • 14
  • 61
  • 76
  • So you have something in the field json that is a byte array and is your file but you don't understand how to save that file? For example, you sent a picture in png form, and that byte array is the picture and you want to save it to the hard drive? – Bailey Miller Aug 21 '17 at 15:44
  • The website and the web api are on two different endpoints, therefore i want to download the document through the website from the web api and save it into a folder. hope you understand what i mean – Wade Martin Aug 21 '17 at 15:48
  • https://stackoverflow.com/a/6397249/5947043 shows you how to save a byte array to a file. You probably just need to read the MIME type from the header to decide what extension to give it. Don't know why you named your variable `json` though, because clearly it isn't. – ADyson Aug 21 '17 at 15:49
  • the variable "byte[] json" is just a name, the feedback should be in bytes tho – Wade Martin Aug 21 '17 at 15:49
  • Well I think you're using the wrong method on WebClient, what about something like OpenRead, which returns a stream which you can then look at the result and find it's mime type – Bailey Miller Aug 21 '17 at 15:50
  • @BaileyMiller do you have any code or recommendation on code that can do that? – Wade Martin Aug 21 '17 at 15:51
  • @WadeMartin Give me like 5 minutes to work on a test, I am going to do the same thing you're doing here and I will use OpenRead and once that passes in a stream you pass the stream to a file stream. – Bailey Miller Aug 21 '17 at 15:52
  • @WadeMartin https://msdn.microsoft.com/en-us/library/781fwaz8(v=vs.110).aspx – Bailey Miller Aug 21 '17 at 15:52
  • @BaileyMiller okay thanks ill await your response – Wade Martin Aug 21 '17 at 15:52
  • @WadeMartin I am finishing up here, I will also clean up some other code that is just a few design notes for other blocks of code. – Bailey Miller Aug 21 '17 at 16:04

1 Answers1

0

Alright so there are a few ways of solving your problem firstly, on the server side of things you can either simply send the content type and leave it at that or you can also send the complete filename which helps you even further.

I have removed the code that is specific to your stuff with basic test code, please just ignore that stuff and use it in terms of your code.

Some design notes here:

            [HttpGet]
            [Route("FileServe")]
            [Authorize(Roles = "Admin,SuperAdmin,Contractor")]
            public async Task<HttpResponseMessage> GetFileAsync(int FileID) //<-- If your method returns Task have it be named with Async in it
            {
                using (var repo = new MBHDocRepository())
                {
                    var file = await repo.GetSpecificFile(FileID);
                    if (file == null)
                    {
                      throw new HttpResponseException(HttpStatusCode.BadRequest);
                    }

            var stream = File.Open(file.PathLocator, FileMode.Open);
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new StreamContent(stream);
            response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName=Path.GetFileName(file.PathLocator)};
            return response;
                }
            }

Your client side code has two options here:

static void Main(string[] args)
        {
            using (var http = new WebClient())
            {


    string url = string.Format("{0}api/FileUpload/FileServe?FileID={1}",webApiUrl, fileId);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
                http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
                var response = http.OpenRead(url);
                var fs = new FileStream(String.Format(@"C:\Users\Bailey Miller\Downloads\{0}", GetName(http.ResponseHeaders)), FileMode.Create);
                response.CopyTo(fs); <-- how to move the stream to the actual file, this is not perfect and there are a lot of better examples

            fs.Flush();
            fs.Close();
            }


        }

        private static object GetName(WebHeaderCollection responseHeaders)
        {

            var c_type = responseHeaders.GetValues("Content-Type"); //<-- do a switch on this and return a really weird file name with the correct extension for the mime type.
        var cd = responseHeaders.GetValues("Content-Disposition")[0].Replace("\"", ""); <-- this gets the attachment type and filename param, also removes illegal character " from filename if present
        return cd.Substring(cd.IndexOf("=")+1); <-- extracts the file name
        }
Bailey Miller
  • 1,376
  • 2
  • 20
  • 36
  • @WadeMartin I think this is going to do it for you. The file saving code could be cleaned up for a more async manner and could be moved to a different thread for better performance. But this gives you two options, decide the file name client side and add the extension based on mime type, or literally take the file name from the server and let the server take care of it. – Bailey Miller Aug 21 '17 at 16:31
  • "responseHeaders.GetValues("Content-Type");" this statements returns a json object. due to the response i am unable to create the right type of object – Wade Martin Aug 21 '17 at 17:17
  • Then set a break point at `return response` and see what the value of `file.FileType` is. If it is set to `application/json` then yeah that makes sense. What does `file.PathLocator` look like? – Bailey Miller Aug 21 '17 at 17:20
  • So `responseHeaders.GetValues("Content-Type")[0] returns an object? Not a string? – Bailey Miller Aug 21 '17 at 17:21
  • the web api method doesnt seem to be being run correctly as it seems that i am unable to put any breakpoints on the method. non of my breakpoints are being reached with the OpenRead method – Wade Martin Aug 21 '17 at 17:24
  • Did you copy my code line for line? Because I left parts out like the authentication, because for testing I didn't want to set that all up. – Bailey Miller Aug 21 '17 at 17:25
  • i left authentication on but removed the http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream"; line – Wade Martin Aug 21 '17 at 17:26
  • There are a few things that haven't been copied over perfectly and therefore won't work correctly. I will try and duplicate them as best as possible. – Bailey Miller Aug 21 '17 at 17:27
  • Let me update my answer with better code that mimics yours as much as possible. – Bailey Miller Aug 21 '17 at 17:27
  • What I recommend is look at my answer, and look for differences, firstly, I added the Content Disposition header. Then on the client side, I am passing in the data to string.format for the url make sure that value is correct. Then set break points at the beginning of each side, client and server and walk through it seeing if it fails. – Bailey Miller Aug 21 '17 at 17:32
  • i can step through the client side but not the server side unfortunatly – Wade Martin Aug 21 '17 at 17:43
  • Does the server side give you an errors? Or what happens on the client side? You hit the openRead line and then what? An exception or? – Bailey Miller Aug 21 '17 at 17:44
  • no exception at all, the openread works and moves to the GetName method which does not seem to have a requestheader of type ["Content-Disposition"] which makes me think that the api isnt being called – Wade Martin Aug 21 '17 at 17:46
  • I would agree with that, so what are you essentially doing? You have 2 visual studios open, the web api is in a different project than this client app, you start both and then the web api is never called? – Bailey Miller Aug 21 '17 at 17:48
  • Are you sure the `url` value is correct? I did make some changes to that part of the code, for better usage of the string.format. Take a look there or even use something like PostMan to send a request to the web api to see if it works. – Bailey Miller Aug 21 '17 at 18:07
  • the PostMan works however will not work with OpenRead – Wade Martin Aug 21 '17 at 19:44
  • I tested this code it worked just fine for me maybe something is different about our api setup overall that had this happen. – Bailey Miller Aug 21 '17 at 19:49
  • Managed to get it to work! created a new controller in my API for it to call directly to that alone and now it works. thank you for your patience! – Wade Martin Aug 21 '17 at 20:04
  • @WadeMartin Hey man computers are weird I am glad that I could help at all, it was kind of fun to work with this code, because I have worked with demo code like this before. – Bailey Miller Aug 21 '17 at 20:17