4

I feel quite stupid at this stage as this doesn't seem like a task i should be battling with, but after little sleep and with a 1000 thoughts running through my mind I think i'm the frog in the pot now with the tunnel-vision-virus clouding the solution. Here goes:

I have a query in PL/SQL that selects data out of a few tables into an XML format (1 row, 1 column being a CLOB type) using DBMS_XMLGEN.getxml. This works perfectly.

My problem comes in returning this XML output to C#, and asking the user where he'd like to save this output as an output.xml file, on his personal pc.

I'm using ASP.net with JQuery (AJAX), thus posting to an .aspx page when a link is clicked and using the returned data (the xml coming from the query in this instance) to save. Using an OLEDB command with an OLEDB datareader doesn't seem to be the correct way...

What is the best way of doing this? I haven't worked a lot with serialization within .net, but i assume this is the best way (according to the research I've done)? Does anyone have an example of how this can be done in my situation?

My thoughts are basically creating this output / file in memory, asking the user where he wants to save it, saving it as a .xml file and clearing the memory.

UPDATE: Ok I managed to create an XMLDocument which easily just accepts the xml output from my query. How could I go about getting input from the user on where to save this file on his pc?

My code so far:

Javascript :

function XML_ExportAppRestrictions() {
try {

    var data = {"action": "exportAppRestrictions"};

    var returned = AjaxLoad('Modules/Common/Common.aspx', null, data, null, null, false);

    if (returned.toString().toUpperCase().substring(0, 5) == "ERROR") {
        return;
    }
    else {
        // should the file be saved here or in my aspx page using xmldoc.save?
        // Using xmldoc.save will only be able to save the file on the server yes?
    }
}
    catch (ex) {
        alert(ex.Message);
    }
}

ASP:

if (Request["action"] == "exportAppRestrictions")
    {

        // Create and open DB connection
        conn.ConnectionString = SG_Common.CommonClass.NetRegUDL;
        common.OpenDBConnection(ref conn);

        // Create command and set SQL statement
        cmd.Connection = conn;
        cmd.CommandType = System.Data.CommandType.Text;
        // select text was removed below due to it's length
        cmd.CommandText = "select x as AppRestrictions from dual";

        rd = cmd.ExecuteReader();

        if (rd.HasRows)
        {
            if (rd.Read())
            {
                str = rd["AppRestrictions"].ToString();
            }
        }
        rd.Close();

        System.Xml.XmlDocument xmldoc = new System.Xml.XmlDocument();
        xmldoc.InnerXml = str;
        xmldoc.Save("c:\\xmltest.xml");

        goto Done;
    }

    // Done runs in a seperate try/catch statement and only returns data to the calling js function.
    Done:
        Response.Write(str);
KDT
  • 671
  • 1
  • 6
  • 15
  • My answer's Update 1 was erroneously for Windows SaveFileDialog. Update 2 shows code for a Web Response with content disposition. – criticalfix Mar 14 '13 at 14:56

2 Answers2

1

The XML should just be UTF8 plaintext. I have used serialization for objects. Maybe someone will correct me, but I haven't found it necessary to use serialization for plain text or binary data.

To bring this data through memory, there's a good StackOverflow answer here showing how to use MemoryStream and byte buffers.

Because if you can get it into a byte[] buffer, you can do something like this:

public bool SaveDocument( Byte[] docbinaryarray, string docname)
{
    string strdocPath;
    strdocPath = "C:\\DocumentDirectory\\" + docname;
    FileStream objfilestream =new FileStream(strdocPath,FileMode.Create,FileAccess.ReadWrite);
    objfilestream.Write(docbinaryarray,0,docbinaryarray.Length);
    objfilestream.Close();
    return true;
}

Refer here for some more info on System.IO methods, e.g.:

StreamWriter writer = new StreamWriter("c:\\KBTest.txt");
writer.WriteLine("File created using StreamWriter class.");
writer.Close();
this.listbox1.Items.Clear();
addListItem("File Written to C:\\KBTest.txt");

UPDATE 1 (Windows):

(I thought you were looking for System.Windows.Forms.SaveFileDialog)

Here is the MSDN sample code for this:

private void button2_Click(object sender, System.EventArgs e)
{
   // Displays a SaveFileDialog so the user can save the Image
   // assigned to Button2.
   SaveFileDialog saveFileDialog1 = new SaveFileDialog();
   saveFileDialog1.Filter = "JPeg Image|*.jpg|Bitmap Image|*.bmp|Gif Image|*.gif";
   saveFileDialog1.Title = "Save an Image File";
   saveFileDialog1.ShowDialog();

   // If the file name is not an empty string open it for saving.
   if(saveFileDialog1.FileName != "")
   {
      // Saves the Image via a FileStream created by the OpenFile method.
      System.IO.FileStream fs = 
         (System.IO.FileStream)saveFileDialog1.OpenFile();
      // Saves the Image in the appropriate ImageFormat based upon the
      // File type selected in the dialog box.
      // NOTE that the FilterIndex property is one-based.
      switch(saveFileDialog1.FilterIndex)
      {
         case 1 : 
             this.button2.Image.Save(fs, 
            System.Drawing.Imaging.ImageFormat.Jpeg);
         break;

         case 2 : 
         this.button2.Image.Save(fs, 
         System.Drawing.Imaging.ImageFormat.Bmp);
         break;

         case 3 : 
         this.button2.Image.Save(fs, 
                System.Drawing.Imaging.ImageFormat.Gif);
         break;
      }

   fs.Close();
   }
}

And, as it says, you need to register the event handler:

this.button2.Click += new System.EventHandler(this.button2_Click);

UPDATE 2 (Web):

Okay, I see you're actually looking for a Web solution. I was thinking Windows instead of Web. Here's what you have to do on the Web side. This is a VB.NET example, but I assume you can translate:

string FileName = "Hello.doc";
string Filename = strFileName.Substring(0, FileName.LastIndexOf("."));                      
Response.AppendHeader("Content-Disposition", "attachment; filename=" + FileName);
Response.TransmitFile(Server.MapPath("~/Folder_Name/" + FileName));
Response.End();

I have done variants on this approach a number of times. You're sending the XML file back to the user's browser. Once it gets there, the user has to decide what to do with it (often, "open this file, or save it?"). That's under the brower's control.

You can help the browser by including a content type header that tells the browser what kind of file it's looking at, and what it should try to open it up with. So this header will ask it to call up Excel:

Response.AppendHeader("Content-Type", "application/vnd.ms-excel");

You'll often see application/pdf, which will pull up Adobe Reader or whatever other PDF reader the user is using. Here you will want to use content type "text/xml". The content type less critical here, because we intend for the user to save the file, rather than open it with an application.

There is a bit of an issue with the content disposition "attachment" versus "inline". Try it both ways and see which one behaves better for you. One of them (I can't remember which) basically ignores the suggested file name you're sending it, and uses the name of your ASP page instead. Very annoying. Firefox at least documents this as a bug, IE apparently just considers it another "feature".

Community
  • 1
  • 1
criticalfix
  • 2,870
  • 1
  • 19
  • 32
  • Thanks for the response ctriticalfix! I wanted to give a +1 but being new to Stackoverflow I can't until I've got a 15+ reputation... :-/ – KDT Mar 18 '13 at 10:09
  • I did however come across a solution to this issue I was having and will post the answer in case anyone else has a need for this in future below. – KDT Mar 18 '13 at 10:22
0

Using Jquery (AJAX) has it's limitations when it comes to user interaction (with specific reference to the save dialog box - i.e. sending a request to an asp page in my example, and expecting a file [or memory stream in my case] back, and wanting to ask the user where he'd like to save it - thus NOT ON the server).

I found a workaround to this as using AJAX for web related calls is my forte. There's some room for improvement as mentioned in this post but worked perfectly for me, thus allowing my XML data to be returned using response.write (x), and this "hack" to allow the user to save the XML file where ever he wants:

Javascript source:

jQuery.download = function (url, data, method) {
//url and data options required
if (url && data) {
    //data can be string of parameters or array/object
    data = typeof data == 'string' ? data : jQuery.param(data);
    //split params into form inputs
    var inputs = '';
    jQuery.each(data.split('&'), function () {
        var pair = this.split('=');
        inputs += '<input type="hidden" name="' + pair[0] + '" value="' + pair[1] + '" />';
    });
    //send request
    jQuery('<form action="' + url + '" method="' + (method || 'post') + '">' + inputs + '</form>')
    .appendTo('body').submit().remove();
};
};

ASP Return:

Response.Clear();
Response.ContentType = "text/xml";
Response.AddHeader("Content-disposition", "attachment; filename=test.xml");
Response.Write(yourXMLstring);

Example to use (javascript call):

$.download("your_url_here", html_url_based_parameters_if_needed_here);

Got this helpful post here: http://www.filamentgroup.com/lab/jquery_plugin_for_requesting_ajax_like_file_downloads/

Hope this helps someone in future!

KDT
  • 671
  • 1
  • 6
  • 15