AJAX File Download with custom header

I want to send a request to a URL which provides me a file download dialog box. At the same time the server needs certain parameters in the request header. I want to insert a custom header in the request and get a file in response. Is there any way we can achieve this?

Answers:

Answer

Try using a element with data-* set to headers for request , $.ajax() with headers option set to a element data-headers object .

At $.ajax() success set a element href to response as Blob within objectURL, download attribute to file.name or temporary file name , call .click() on a element to activate "Save File" dialog .

$(document).ready(function() {
  $("input[type=button]").click(function() {
    // set `data-headers` object
    var el = $("[data-headers]");
    el.data("headers")["x-custom-headers"] = $("input[type=text]")[0].value 
      || el.data("headers")["x-custom-headers"];
    $.ajax({
      url: URL.createObjectURL(new Blob([$("textarea").val()], {
        type: "text/plain"
      })),
      type: "GET",
      // set `headers` to `a` element `data-headers` object
      headers: $("[data-headers]").data("headers"),
      beforeSend: function(jqxhr) {
        console.log(this.headers);
        alert("custom headers" + JSON.stringify(this.headers));
      },
      success: function(data, textStatus, jqxhr) {
        var file = new Blob([data], {
          "type": jqxhr.getResponseHeader("Content-Type")
        });
        console.log(data, file);
        $("textarea, input[type=text]").val("");
        $("a")
          .attr({
            "href": URL.createObjectURL(file),
            "download": file.name || "file-" + $.now()
          }).on("click", function() {
            $(this).remove()
          })
          .get(0).click();
      },
      error: function(jqxhr, textStatus, errorThrown) {
        console.log(textStatus, errorThrown)
      }
    });
  })
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<textarea maxlength="5" placeholder="echo"></textarea>
<br>
<!-- set `x-custom-header` to `input type="text"` value -->
<input type="text" maxlength="5" placeholder="set request header" />
<br />
<input type="button" value="download" />
<!-- set default `x-custom-header` to "123" -->
<a data-headers='{"x-custom-headers":"123"}'></a>

Answer

From what I understand is that you want to request a file from the web that requires a authentication header but can't be downloaded with a simple link.

What you need to do then is to make a ajax call with the authentication header to download the data as a blob. Then save the data from the client side


jQuery's ajax/get methods dosen't support the responseType = blob

So this example is using the latest Fetch API in Blink and Firefox. It also uses the FileSaver.js to save a client-side generated blob in versus ways to handle cross browser issues.

fetch("/", {headers: {"X-mashape-key": "XXXXXX-4-YYYYYY"}})
    .then( response => {
        // we can get the filename if the
        // response headers contains Content-Disposition
        var cd = response.headers.get("Content-Disposition");
        var name = cd && cd.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1];

        response.blob().then( 
            blob => window.saveAs(blob, name || "index.html")
        )
    })

It also use some new suger syntax that is available in es6 that can be transpiled to es5 with babel or traceur

If you don't include a fetch polyfill and need to support older browsers - Then you need to do it the old fashion way with XHR

var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.open("get", "/");
xhr.setRequestHeader("X-mashape-key", "XXXXXX-4-YYYYYY");
xhr.send();
xhr.onload = function(){
    var cd = xhr.getResponseHeader("Content-Disposition")
    var name = cd && cd.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1];

    window.saveAs(xhr.response, name || "index.html")
}
Answer

Maybe I don't get your point.

What I think is like this:

//server xxx.php
//omit some code
echo '<a href="file_path" id="download">file_name</a>';

//browser
ajax({
    url: 'http://xxxx/xxx.php',
    success: function(html){
        //append to dom and TODO other manipulations
        $(dom).append(html);
        $('#download').trigger('click');
        $(dom).remove(html);
    }
})
Answer

Ok, I checked in an old application I had and here is a sample. You'll need to adapt but the idea is here. This first part is part of an Request interceptor attribute that is placed on the WebMethod. The whole interceptor is quite complex because it manages several cases.

int byteSize = 262144;
byte[] data = new byte[byteSize];

if (!String.IsNullOrEmpty(myfileName))
{
    _context.Response.AddHeader("Content-disposition", "attachment; filename=\"" + myfileName+ "\"");
}

_context.Response.ContentType = DefaultContentType;
_context.Response.Cache.SetCacheability(HttpCacheability.Private);
_context.Response.Cache.SetExpires(DateTime.UtcNow.AddHours(1));
_context.Response.Buffer = false;
_context.Response.BufferOutput = false;
_context.Response.AddHeader("Content-length", myStreamSize.ToString());

try
{
    int counter = 0;
    int bytesRead = mystream.Read(data, 0, byteSize);
    while ((long)counter < myStreamSize|| bytesRead > 0)
    {
        counter += bytesRead;
        if (bytesRead < byteSize)
        {
            byte[] outBuf = new byte[bytesRead];
            Array.Copy(data, outBuf, bytesRead);
            _context.Response.BinaryWrite(outBuf);
        }
        else
        {
            _context.Response.BinaryWrite(data);
        }
        bytesRead = mystream.Read(data, 0, byteSize);
    }
}
finally
{
    mystream.Close();
    mystream.Dispose();
}
_context.Response.End();

This is the second part in the Javascript on the client side. We used ExtJS instead of jQuery.

downloadFile: function(url, getArgs) {
    if( !this.iframe )
    {
        this.iframe = document.createElement("iframe");
        this.iframe.className = "x-hidden";
        document.body.appendChild(this.iframe);
    }

    var args = Ext.urlEncode(getArgs);
    url = url + (args ? ("?" + args) : "");
    this.iframe.src = url;
}

The click of a button would call this function and give it the URL to the WebMethod and eventually extra args to pass. The idea on the JS side is to create a hidden iFrame that would take the URL and would then prompt the user to download.

I hope it helps out

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.