programmatically change FolderName in FileSaver Adapter

Feb 18, 2008 at 5:30 PM
Hi, great control... I love the potential for extending with the adapters. I'm trying to get to the bottom of something fairly frustrating, perhaps someone can help. I'd like to be able to feed the FileSaver Adapter a folder name at Runtime - i.e. a user would be able to select a different location on the server to save a file. I was hoping to use something along the lines of

((com.flajaxian.FileSaverAdapter)FileUploader1.Adapters0).FolderName = ddlFolderSelectedValue;

but it appears to get ignored by the control. Any ideas? I appreciate the assist.
Coordinator
Feb 18, 2008 at 5:45 PM
Edited Feb 18, 2008 at 5:48 PM
Hi Tucker,

I have just tried it with the following code and it works well. May be you are setting it too late in preload.

<%@ Page Language="C#" AutoEventWireup="false" %>
<%@ Register TagPrefix="fjx" Namespace="com.flajaxian" Assembly="com.flajaxian.FileUploader" %>
<SCRIPT language="C#" runat="server">
override protected void OnInit(EventArgs e) {
this.Load += new System.EventHandler(this.Page_Load);
}
protected void Page_Load(Object sender, EventArgs e){
((com.flajaxian.FileSaverAdapter)FileUploader1.Adapters0).FolderName = "UploadFolder2";
}
</SCRIPT>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<fjx:FileUploader ID="FileUploader1" IsDebug="true" runat="server" >
<Adapters>
<fjx:FileSaverAdapter Runat="server" FolderName="UploadFolder" />
</Adapters>
</fjx:FileUploader>
</div>
</form>
</body>
</html>
Feb 18, 2008 at 7:29 PM
Edited Feb 18, 2008 at 8:55 PM
Hmm, interesting, thanks for the reply. After trying a few things and looking at your code, I've discovered a couple of things - maybe you can help clear some things up.

First of all, the reason I wasn't able to change the directory at page load appears to be that it will not work if I place the code inside a
if(Request.IsAuthenticated){} block. When I take the line out of there, it changes the directory as it should. Not ideal, but I'm sure I can find a way around this.

However, the issue I'm still having is that I'd like the folder's ID to come from a dropdown control on the page - and no matter what the selected value, it appears the FileUpload control always gets the value at index 0. Any suggestions? Thanks again!

update
Just for the heck of it, I've also tried passing in a variable name via Request.QueryString. When I have something such as
((com.flajaxian.FileSaverAdapter)FileUploader1.Adapters0).FolderName = "~/Folder" + Request"extra";
the file does get written to "Folder", but not to the parameter specified in ?extra=X via the querystring.
Also, if I do a
Response.Write(((com.flajaxian.CustomUpAdapter)FileUploader1.Adapters0).FolderName.ToString());
immediately after setting the folder, it outputs the correct string - but the file does not go where I want when it gets uploaded. Strange, indeed.
Coordinator
Feb 18, 2008 at 10:53 PM
Hi Tucker, it seems you have very specific code, if you can strip it from copyright sensitive source and e-mail it to me I can have a look at it later but it is hard to guess now without knowing what your code is doing...
Feb 19, 2008 at 4:02 PM
Sure thing, right now I'm just working with a proof-of-concept, so I don't mind sharing for public consumption so we can get to the bottom of this issue. Essentially what I'm doing is building a tool to upload photos to stories in our CMS, which stores photos in a different folders for each story ID. I have a dropdown list that has the story IDs as its values, I want to select a story and then upload. I've created a new adapter based on FileSaverAdapter with a few extra lines to check for an existing folder and create if necessary, but for testing purposes I've created the folders manually and reverted back to the original FileSaverAdapter to make sure it's not my adapter causing the issue. Here goes:

code-behind
DataLayer dl = new DataLayer();
protected void Page_Load(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
divPhoto.Visible = true;

ddlStories.DataSource = dl.GetHeadlines(10); //Get headlines and bind them to the dropdown list
ddlStories.DataBind();
}
((com.flajaxian.CustomUpAdapter)FileUploader1.Adapters0).FolderName = "~/Images/" + ddlStories.SelectedValue;
Response.Write(((com.flajaxian.CustomUpAdapter)FileUploader1.Adapters0).FolderName.ToString()); //oddly, this actually outputs the right path
}


.aspx file
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="photo.aspx.cs" Inherits="Admin_photo" %>
<%@ Register TagPrefix="fjx" Namespace="com.flajaxian" Assembly="com.flajaxian.FileUploader" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Upload Photos</title>
</head>
<body>
<form id="form1" runat="server">
<div runat="server" id="divPhoto" visible="false">
<asp:DropDownList ID="ddlStories" runat="server" DataTextField="Headline"
DataValueField="ScrapbookID" AppendDataBoundItems="true" AutoPostBack="True">
<asp:ListItem Text="Select a story..." Value="0" />
</asp:DropDownList>
<br />

<fjx:FileUploader ID="FileUploader1" runat="server" IsDebug="true">
<Adapters>
<fjx:FileUploadAdapter Runat="server" FolderName="~/Images" />
</Adapters>
</fjx:FileUploader>
</div>
</form>
</body>
</html>

Ok, so here's what it's doing: While the Response.Write outputs the desired path correctly, when it uploads, it always uploads to Images/0 regardless of what's selected - I changed the value of index position 0 in the dropdown list, and it goes there. For additional testing, I tried removing the one static listitem, and it does not pull any value from the list on upload. However, if in the same line I add something else to the folder string, THAT part of it stays. So it would appear that when I click upload in the control, it "forgets" the bindings and values of the dropdown.

Thanks, if you need any more info I'd be happy to provide!
Coordinator
Feb 20, 2008 at 3:52 AM
Edited Feb 20, 2008 at 4:35 AM
OK I understand that the component is missing a way to transfer state while uploading th file, I have added a state property to help with that. You can use it as

FileUploader1.State.Add("MyKey", "MyValue");

then when th file is transfered you can get it as

if(FileUploader1.FileIsPosted){
string stateInfo = this.Request.QueryString["MyKey"];
}

of here is a full example

<%@ Page Language="C#" AutoEventWireup="false" %>
<%@ Register TagPrefix="fjx" Namespace="com.flajaxian" Assembly="com.flajaxian.FileUploader" %>
<%@ Import Namespace="System.Collections.Generic" %>
<SCRIPT language="C#" runat="server">
override protected void OnInit(EventArgs e) {
this.Load += new System.EventHandler(this.Page_Load);
}
protected void Page_Load(Object sender, EventArgs e){
if(!this.IsPostBack){
ddl1.DataSource = new KeyValuePair<string, string>[]{
new KeyValuePair<string, string>("UploadFolder1", "UploadFolder1"),
new KeyValuePair<string, string>("UploadFolder2", "UploadFolder2"),
new KeyValuePair<string, string>("UploadFolder3", "UploadFolder3")
};
ddl1.DataBind();
}

if(FileUploader1.FileIsPosted){
((com.flajaxian.FileSaverAdapter)FileUploader1.Adapters[0]).FolderName = this.Request["SelectedFolder"];
}else{
FileUploader1.State.Add("SelectedFolder", ddl1.SelectedValue);
}
}
</SCRIPT>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="ddl1" runat="server" DataValueField="Key" DataTextField="Value" AutoPostBack="true" />

<div> 
Feb 20, 2008 at 3:40 PM
Well, there we have it! Works brilliantly, thanks. I just made these changes, and the app behaves as intended. I also tested in IE7 and it works there as well. Thanks again for your work on this; I will continue with my implementation and post here if I encounter any additional issues. If you'd like, I can post some samples of my app and adapter, which will include some writes to a SQL database, to help expand the documentation for this control. I'd been looking for something precisely like this for quite a long time, so I'm happy to help others discover it and use it to its full potential.
Coordinator
Feb 20, 2008 at 4:46 PM
Thank you Tucker, sure anything you can post that would help others would be a great idea!
Feb 20, 2008 at 6:36 PM
Another question on this topic with the new State.Add method.... I'm running a SQL query after the file is received in the function I specify in the OnFileReceived property. Based on your example, I added three other properties to State, however it seems not all of them are making it through to OnFileReceived. I've got the other parameters being set at the same time as the "SelectedFolder, but that's the only one that I can properly find with this.Request. Any clues why that might be? Here's an equivalent set of code:

if (FileUploader1.FileIsPosted)
{
((com.flajaxian.CustomUpAdapter)FileUploader1.Adapters0).FolderName = this.Request"SelectedFolder"; //this works fine, by the way
}
else
{
FileUploader1.State.Add("SelectedFolder", ddlStories.SelectedValue);
FileUploader1.State.Add("Caption", txtCaption.Text);
FileUploader1.State.Add("Source", txtSource.Text);
FileUploader1.State.Add("Type", ddlType.SelectedValue);
}
...
protected void FileReceived(object sender, EventArgs e)
{
...
com.flajaxian.FileReceivedEventArgs file = (com.flajaxian.FileReceivedEventArgs)e;

string sqlstring = .... this.Request"SelectedFolder"; //that goes in fine
sqlstring += file.File.FileName; //works great
sqlstring += this.Request"Caption"; //no dice
...
}

Coordinator
Feb 20, 2008 at 8:31 PM
The reason it doesn't work is because your ddlStories is set to be AutoPostBack=true and the text fields are not. So you have at least 2 options here:
1. Set text fields to postback on change (easier but more resource intense)
2. Use the client side state management - the functionality I have just added.

You use it on the client side (JavaScript) by calling
window.FileUploader1.setStateVariable('t1', "Text");

Or this is a complete example:
<%@ Page Language="C#" AutoEventWireup="false" %>
<%@ Register TagPrefix="fjx" Namespace="com.flajaxian" Assembly="com.flajaxian.FileUploader" %>
<%@ Import Namespace="System.Collections.Generic" %>
<SCRIPT language="C#" runat="server">
protected void FileReceived(Object sender, EventArgs e){
string t1 = this.Request["t1"];
}
</SCRIPT>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="t1" runat="server" onChange="window.FileUploader1.setStateVariable('t1', this.value)"></asp:TextBox>

<div> 
Feb 20, 2008 at 9:21 PM
Ah, phenomenal! Thank you sir, this has been quite the beta cycle thus far!
Feb 21, 2008 at 1:03 PM
Hello Flajaxians.

Thanks for implementing the state option. As this program is called Flajaxian I would like to point out a last error I found: Try to put a flajaxian object inside a Ajax Control Toolkit TabContainer panel. It won't work.