3 votes

download file from url with ajax and asp.net mvc 5

I am trying to download a file from a url with ajax and asp.net mvc 5 this is my controller:

[HttpPost]
public ActionResult DownloadDocument(DownloadModel model){
   urlFile = "http://www.storage.net/data/pdf-xml/" + model.nombre.ToLower() + model.Extencion;
   //Create a stream for the file
   Stream stream = null;
   //This controls how many bytes to read at a time and send to the client
   int bytesToRead = 10000;
   // Buffer to read bytes in chunk size specified above
   byte[] buffers = new Byte[bytesToRead];
   // The number of bytes read
   try{
     //Create a WebRequest to get the file
     HttpWebRequest fileReq = (HttpWebRequest)HttpWebRequest.Create(urlFile);
     //Create a response for this request
     HttpWebResponse fileResp = (HttpWebResponse)fileReq.GetResponse();
     if (fileResp.ContentLength > 0)//aquí reviso si el archivo contiene datos (existe o no)
     {
       if (fileReq.ContentLength > 0)
         fileResp.ContentLength = fileReq.ContentLength;
       //Get the Stream returned from the response
       stream = fileResp.GetResponseStream();
       // prepare the response to the client. resp is the client Response
       //var resp = HttpContext.Current.Response;
       //Indicate the type of data being sent
       Response.ContentType = "application/octet-stream";
       //Name the file 
       Response.AddHeader("Content-Disposition", "attachment; filename=\"" + model.nombre + model.Extencion + "\"");
       Response.AddHeader("Content-Length", fileResp.ContentLength.ToString());
       int length;
       do{
         // Verify that the client is connected.
         if (Response.IsClientConnected){
           // Read data into the buffer.
           length = stream.Read(buffers, 0, bytesToRead);
           // and write it out to the response's output stream
           Response.OutputStream.Write(buffers, 0, length);
           // Flush the data
           Response.Flush();
           //Clear the buffer
           buffers = new Byte[bytesToRead];
         }else{
           // cancel the download if client has disconnected
           length = -1;
         }
       } while (length > 0); //Repeat until no data is read
     }
   }catch (Exception e){
     d2.Add("Error", e.Message);
   }finally{
     if (stream != null){
       //Close the input stream
       stream.Close();
     }else if (d2.Count < 1){
       d2.Add("Error", "El Archivo no existe");
     }
   }
   //mostramso el mensaje de que no existe el archivo
   return Json(serializer.Serialize(d2));
 }  

If the form is directed to the controller it performs the download, but if a file does not exist it will send me a message which I display in my view but it is displayed in a separate view.
And I want the download to be done through ajax of jquery because that is the way I can show the message, but I do not see the download in the browser.
This is my form:

<form method="post" class="form-horizontal" role="form" name="formDownload" id="formDownload140464">
  <input type="hidden" value="AAA010101AAA" id="rfcCompany" name="rfcCompany">
  <input type="hidden" value="40464" id="id" name="id">
  <input type="hidden" value=".pdf" id="Extencion" name="Extencion">
  <input id="Nombre" name="Nombre">
  <button type="button" class="btn btn-primary btn-sm previewPopover" title="Descargar" onclick="MostrarMensaje(40464)">
    <span class="elusive icon-download-alt"></span>
  </button>
</form> 

My ajax function is this, I use it to display the message.

function MostrarMensaje(id) {
    $('#formDownload1' + id).on("submit", function (e) {
        e.preventDefault();
        $.ajax({
            type: 'POST',
            url: '/Portal/DownloadDocument',
            data: $('#formDownload1' + id).serialize(),
            success: function (data) {
                var d = JSON.parse(data);
                $.jGrowl("" + d["Error"], {
                    header: "Error",
                    //sticky: true,
                    theme: "red"
                });
            }
        });
    });
} 

This displays the message but does not download.
I have seen that many people pass the url in the data and do the download with window.location.href = d["Url"]; but my problem here with this code is that I won't know if the file exists or not and it will download an empty file.

2voto

Asier Villanueva Points 13318

You cannot open the browser download dialog from JavaScript. You have to navigate to the document for the browser to open the download dialog.

If you want to check beforehand if the file exists what you can do is to make an action that checks the existence of the file. This way you call the action through ajax to check if the file exists and, if so, you navigate to the download url with document.location.

Another alternative to open the download dialog is the HTML5 download attribute that allows you to create a link that downloads a file but it would not fit your case and it is not yet supported by all browsers. Chrome, Firefox, Edge and Opera do support it, but not Internet Explorer in any of its versions and if I remember correctly neither Safari.

0voto

Leandro Tuttini Points 25288

It is not valid to send a byte array as a json for downloading, but send it as a file.

[HttpPost]
public FileResult DownloadDocument(DownloadModel model){
  //resto codigo
  return File(...)
}

ASP.NET MVC Uploading and Downloading Files

and use a library such as

jquery.fileDownload

to download using javascript

HolaDevs.com

HolaDevs is an online community of programmers and software lovers.
You can check other people responses or create a new question if you don't find a solution

Powered by:

X