0

So, what I want to do is add a link to a web-page which, when clicked, will call a generic handler that will create the required data in memory and serve it back to the user as a stream of some sort, who is then prompted to Save it as a file.

SO, we might have: <a onclick=”makeme()”>Clickety</a>

and the JavaSctipt makeme() function is just

 function makeme() {
    $.ajax({
        url: 'makefile.ashx',
        cache: false
    }).done(function (response) {
       //alert(repsonse)
    }); 
 }

In the handler, I've tried:

  Dim rtn As String = "1,2,3,4,5"
  Dim bArray As Byte() = System.Text.Encoding.Default.GetBytes(rtn.ToCharArray())
  ' or Dim mstream As New MemoryStream(bArray)   ‘ alt (see below)
  ' either of the next two lines
  context.Response.ContentType = "application/octet-stream"
  context.Response.ContentType = "application/force-download"
  context.Response.AddHeader("Content-Length", bArray.Length.ToString())
  context.Response.AddHeader("Content-Disposition", "filename.csv")
  context.Response.BinaryWrite(bArray)
  ' or context.Response.Write(mstream)           ‘ if alt used above

Nothing appears to happen though....

even simpler, this:

  Dim rtn As String = "1,2,3,4,5"
  context.Response.Clear()
  context.Response.ContentType = "text/csv"
  context.Response.AddHeader("Content-Disposition", "attachment;filename=myfilename.csv")
  context.Response.Write(rtn)

also though, nothing happens on clicking the link - I mean, the handler is called, but that's it.

PSU
  • 175
  • 1
  • 9
  • There are a *lot* of possibly duplicate questions that show how to return a CSV file. These show what the correct content type is, that downloading is something specified in the *content disposition* header, not the content-type. Unless you have a Web Forms application, there's no reason to create a handler either. – Panagiotis Kanavos May 27 '19 at 10:36
  • What are you trying to do? It's easier to point to the correct way to create and send a CSV file than fix the problems in this code, like the duplicate content type header, the incorrect disposition and worse, a missing `charset` header while using the server's codepage (which is definitely not UTF8) to create the byte buffer – Panagiotis Kanavos May 27 '19 at 10:39
  • All the questions I've seen return a file that has been created and saved on the server first - and I am trying to avoid doing that. And yes, this is a web forms application. – PSU May 27 '19 at 10:50
  • It doesn't matter whether it's a file or not as long as you can create a stream on top of it. There are such questions about Webforms too. In fact, you can write directly to the response stream. That takes care only of the first couple of lines though, not the problems with the rest of the code. – Panagiotis Kanavos May 27 '19 at 10:56
  • Have you considered *not* using Webforms for this? That stack was never meant for REST/AJAX style calls. What you ask is a simple `return File(someStream...)` in a Web API action – Panagiotis Kanavos May 27 '19 at 10:58
  • Possible duplicate of [Generate CSV file in ASP.Net](https://stackoverflow.com/questions/13244875/generate-csv-file-in-asp-net) – Panagiotis Kanavos May 27 '19 at 10:58
  • In any case, the duplicate question is the very first result if you google for `webforms return csv file`. The string is generated using a `StringBuilder` and the *actual* code needed to return are the last 5 lines – Panagiotis Kanavos May 27 '19 at 10:59
  • It's an old application, so I haven't much choice about the technology. And the question you directed me to may work on a postback within a webform, but not from a generic handler, which is what I'm trying to achieve. – PSU May 27 '19 at 11:08
  • Did you try it? The code that returns the CSV string is the last five lines. Your own code is using the `Response` object – Panagiotis Kanavos May 27 '19 at 11:09
  • Yes, I did try it! – PSU May 27 '19 at 11:10
  • Then *post what you tried* and what went wrong. The code in the question has no chance of working. It will generate invalid responses if it doesn't throw a runtime due to the invalid content types – Panagiotis Kanavos May 27 '19 at 11:11
  • Well, form the example simply as per my edit on the original question – PSU May 27 '19 at 11:12
  • Another duplicate, this time with a handler, although the important code remains the same : [ASP.NET file download from server](https://stackoverflow.com/questions/18477398/asp-net-file-download-from-server) – Panagiotis Kanavos May 27 '19 at 11:14
  • That last example is sending back a file that has been written to the server - what I am trying to avoid doing. – PSU May 27 '19 at 11:16
  • `nothing happens on clicking the link - I mean, the handler is called, but that's it.` is not a problem description. Was there an error? How did the Javascript function call the URL? What did it do with the returned data? – Panagiotis Kanavos May 27 '19 at 11:16
  • So use `Write` instead of TransferFile. You still have to use `Response` you still have to use the correct headers. If the code actually runs, check your *client* code. Use a debugging proxy like Fiddler to see what's actually returned by the server. Is it a file? Or is it a 500 error? – Panagiotis Kanavos May 27 '19 at 11:18
  • The handler is called via simple JavaScript function - I know it's being called successfully. No errors are raised in the handler, but I see nothing - no prompt to save whatever is being returned, or anything. – PSU May 27 '19 at 11:19
  • Also check your Javascript. You can't just execute a `fetch` and get the download prompt to pop up. Your call will retrieve the server's result, it won't pop up the download prompt. That's up to *your* javascript code. – Panagiotis Kanavos May 27 '19 at 11:21
  • Check for example [Prompt file download](https://stackoverflow.com/questions/40662885/prompt-file-download) or [Download File Using Javascript/jQuery](https://stackoverflow.com/questions/3749231/download-file-using-javascript-jquery) – Panagiotis Kanavos May 27 '19 at 11:23
  • I was hoping that's what the "application/force-download" ContentType would do... thing is, as there isn't a file to direct a download prompt to, I can't add a download link in the JS. – PSU May 27 '19 at 11:27
  • Basically, what I'm trying to do is return a stream/string of data and get the page to prompt the user to save it is as a file - with the headers telling it what kind of file it should be saved as. – PSU May 27 '19 at 11:29
  • Maybe that can't be done. – PSU May 27 '19 at 11:30
  • I've already posted links that show just that. I've also posted links to questions that show how you can trigger the download prompt in code. You haven't posted *your* code yet though, both *client* and server, so it's impossible to reproduce the issue – Panagiotis Kanavos May 27 '19 at 11:36
  • Try isolating the problem. Just type the URL you use in your GET in the browser's address bar. What happens? What do you get back? What does Fiddler or the browser's Network tab in the Developer Tools show? – Panagiotis Kanavos May 27 '19 at 11:38
  • I've added the JS call - the client side code is nothing more than in my OP now, and the same goes for the server side code. There's nothing else to it. All the links you've directed me to are about returning a file on the server, which isn't what I want. (But thanks anyway.) I'll have a play on Fiddler and see if that can shed any light on the issue. – PSU May 27 '19 at 11:45
  • 1
    As you suggested - just typing the URL called in the JS call directly into the address bar does work! Bugger me - it's that simple: just changing my JS code to location.href= 'makefile.ashx'; does the trick. No fancy AJAX. Sigh. Thanks again. – PSU May 27 '19 at 11:58
  • 2
    [I should know by now....](https://i.pinimg.com/736x/7e/e6/1b/7ee61b213ab62a3c6179b78020b35da6--linus-peanuts-peanuts-comics.jpg) – PSU May 27 '19 at 13:08
  • I'd suggest just using the `href` attribute instead of `onclick` and `window.location`. Simpler and easier to read. – crenshaw-dev May 27 '19 at 15:15

1 Answers1

1

The problem is that jQuery's $.ajax method was never intended to trigger the download of a file. It's meant to download data to later be handled in JavaScript.

You should point the anchor's href attribute directly to the handler. Then the browser will notice the response's content-disposition and handle the file download.

<a href="makefile.ashx">Clickety</a>
crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81