Question

I am working on a Web Service where the user would input parameters and have the option of have the data returned in various file formats (xml, html (on screen), csv, etc.).

If the server generates the file content (and therefor there is no actual file), then returning a URL to the file is not an option. So how can this be done? I know there are xml attributes to indicate that the data within an element is binary, but that won't trigger the browser to download the data as a file.

Is there some equivalent of Content Disposition for XML/Web Services?

On a very related note:

Can javascript/AJAX trigger a browser to download a file? I know that js alone doesn't have the ability to "generate" files (in the sense that the browser will download a string as a file with a given file name" but what if an ajax function calls a script and the script reply has Headers set to download?

I'll experiment with that last part, but would like to know if there is any solid info already out there.


More Details:

First, thank you to everyone who has responded. All of the answers have been really helpful and educational.

I thought I might try to explain a bit more about the situation to tie up all of the comments/concerns/suggestions that span the answers.

I manage a project for an internal staff web site. The project deals mainly with schedule viewing and modification. There are at least 4 different PHP scripts which query a MySQL DB for essentially the same data. The queries vary slightly based on the context. In one case it pulls a daily schedule which groups by the assignment, in another it queries a full week's schedule and groups by person. In yet another it queries for just one person's weekly schedule. In some it outputs data like hourly totals per person or day or position, and in others it just gives what is stored in the DB.

I have started to feel guilty about certain aspects of the project. Obviously I can combine a lot of the data fetching into one included script. I probably ought to be using object-oriented PHP to handle the results. And I have been more focused on making this mess work than making sure the data is safe and secure and that the stress on the DB is minimal and optimized. I'm not even sure how to really know if the queries ARE optimized.

And in addition to the above mentioned guilt, I've really started to get confused. In trying to neaten things up, I've found myself tangled up in a knot where one fix screws up something that was working fine.

So, I thought a good solution would be to create a Web Service that would simplify the entire process and force me to have a logic (an API, even) to how the data is fetched, and would be more useful for anyone who tried to take over when I eventually am not the lead developer. Plus it would make the whole thing more portable, for both upgrades and shifts in the site's back-end (some day it might be on a Windows server using Cold Fusion but I shudder at the thought) and for other software that may want to connect with the database.

But here's the rub:

The entire genesis of this project was based on the fact that our previous scheduling solution was to upload the very ugly and non-semantic HTML report generated by the very expensive enterprise-level scheduling software we use. The number one problem we had with this hideous HTML report and the software that generated it was that there was no option for a .ics "icalendar" file that we could import into Outlook or Google Calendar. I made it my personal mission to figure out a way to turn those awful reports INTO ics files, and thus begins the story on why I started learning PHP, regex, and MySQL.

So, if you aren't bored and asleep yet, I think a Web Service is the best option for simplifying and streamlining the schedule process, but if I end up having to use plain PHP/MySQL to get the most valuable part of the data (the calendar files) then I'm inclined to just ditch the whole thing and keep untangling the knot.

I do understand, by the way, that the script that uses the Web Service can create the ics file and that the AJAX can simply be avoided when that is what the user is requesting. Again, thanks to all that have made this more clear to me. I just want the web service to handle everything returned by the DB as possible on its end to have a sense of a clear partition between the HTML/JS/PHP and the database.

Thanks again!

Was it helpful?

Solution

To have the browser download the file, you can do this:

window.location = "http://example.com/webservice-file-url";

And use the header:

Content-Disposition: attachment; filename="filename.ext"

If you want the user to choose between viewing on-screen and downloading, just use a conditional:

if (requested_format == 'html') {
    //get an html version and show it on screen
    ajaxRequestForHTML();
} else {
    //download the file
    window.location = "http://example.com/webservice-file-url";
}

OTHER TIPS

I'm afraid that will not work. AJAX calls doesn't pass their response headers to the browser. The only thing you can if you change the headers is that your AJAX call may fail because the headers won't be recognized.

The only solution I see is to return a URL to a file and open new JS window that will trigger that download.

Return a byte array from the web service. The same if you want to send files to the service.

In a typical browser environment, JavaScript cannot (for security reasons) access the clipboard or the filesystem. For some discussion see clipboard-image-directly-save-in-to-client-side-using-javascript, or similar questions.

There are exceptions, for example can-javascript-access-a-filesystem.

The point of Web Services is that they are not meant to be accessed directly by a browser - they are an API for other web applications to use. Thus you should probably write a user-friendly front-end which calls the web service and serves up the data as a file within a HTTP response. That will trigger the browser to download it as a file.

Are you sure you want this to be a web service? How about a normal web page, with form fields for the parameters, which just returns the data as a file, with the appropriate content-disposition?


Based on your update, I'm pretty certain that you do not want a web service to return the .ics file. What you want is a script that will accept some parameters using the query string, and which will then return the file, using the "Attachment" content-disposition. This will cause the user to be prompted to open the .ics file.

Now, you also have the great idea to refactor your several similar scripts to reduce redundancy and maintenance. However, I'm concerned about what you said about every change breaking something. Before you start refactoring, you need to build yourself at least a small test suite, preferably automated. That way, you'd run the tests after every small change, to see if you'd broken anything yet. This way, when you break something, you can only have broken an hour's worth.

I would recommend refactoring the PHP scripts using included scripts or whatever mechanisms PHP has for reuse (I don't know PHP). You may then find that some of the common code would be appropriately implemented as a web service.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top