Jun
25
Written by:
Michael Washington
6/25/2011 9:28 AM
This article describes how you can upload files using LightSwitch, and store them on the server hard drive. This is different from uploading files and storing them in the server database.
If you want to upload files and store them in the database, see How Do I: Import and Store a Data File by Matt Sampson.
The sample application contains an Upload File button.
This opens a file upload popup (this uses code from the Matt Sampson article).
After uploading the file, the file shows up in the File Records list.
Clicking on a file, then clicking the Download File button, will download the file.
Note: This example only works when the LightSwitch application is configured as a Web Application.
In the Visual Studio Solution Explorer, when we switch to File View…
We can see files were added to the ServerGenerated project, that provides the upload and download functionality.
If we unload the project…
…and edit the project file…
…we see the entries that were added to include the files in the project. Without doing this, the .ascx and .ashx files that were added would not be in the deployed project and the program would not work.
Note however, that any .cs code files are automatically compiled into assemblies and deployed. There is no need to reference them in the project file.
Don’t forget to reload the project!
When we publish the project…
The upload and download files are also deployed.
The Screen
To explore the code, let us first start with the Screen.
Displaying Files (WCF RIA Service)
To display the files that have been uploaded, we use a simple WCF RIA Service. See this article: Creating a Simple LightSwitch RIA Service (using POCO) for directions on creating a WCF RIA Service and consuming it in a LightSwitch application.
This code is used in the WCF RIA Service:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.IO;
using System.Web.Hosting;
// TODO: Create methods containing your application logic.
// TODO: add the EnableClientAccessAttribute to this class to expose this DomainService to clients.
public class FileDomainService : DomainService
{
[Query(IsDefault = true)]
public IQueryable<FileRecord> GetFileRecords()
{
return _LocalFiles.AsQueryable();
}
private readonly List<FileRecord> _LocalFiles;
public FileDomainService()
{
_LocalFiles = new List<FileRecord>();
string strFileDirectory= HostingEnvironment.MapPath(@"~\");
// Find instance of "bin"
int intPositionOfBin = strFileDirectory.ToLower().IndexOf("bin");
if (intPositionOfBin > -1)
{
strFileDirectory = strFileDirectory.Substring(0, intPositionOfBin);
}
strFileDirectory = strFileDirectory + @"Files\";
EnsureDirectory(new System.IO.DirectoryInfo(strFileDirectory));
// Add all directories at this directory.
DirectoryInfo objDirectoryInfo = new DirectoryInfo(strFileDirectory);
string FolderName = objDirectoryInfo.Name;
// Get the files in the directory
foreach (var fi in objDirectoryInfo.EnumerateFiles().OrderBy(x => x.Name))
{
FileRecord objFileRecord = new FileRecord();
objFileRecord.FileName = fi.Name;
// Add file to the collection
_LocalFiles.Add(objFileRecord);
}
}
#region EnsureDirectory
public static void EnsureDirectory(System.IO.DirectoryInfo oDirInfo)
{
if (oDirInfo.Parent != null)
EnsureDirectory(oDirInfo.Parent);
if (!oDirInfo.Exists)
{
oDirInfo.Create();
}
}
#endregion
}
public class FileRecord
{
[Key]
public string FileName { get; set; }
}
Also, note the References that the project requires.
Uploading Files
We right-click on the Upload File button and select Edit Execute Code.
We use the .xaml popup, and code from the Matt Sampson article:
/// from: http://blogs.msdn.com/b/rmattsampson/archive/2011/05/23/how-to-import-and-store-a-data-file.aspx
partial void ImportAFile_Execute()
{
// To invoke our own dialog, we have to do this inside of the "Main" Dispatcher
// And, since this is a web application, we can't directly invoke the Silverlight OpenFileDialog
// class, we have to first invoke our own Silverlight custom control (i.e. SelectFileWindow)
// and that control will be able to invoke the OpenFileDialog class (via the Browse button)
Dispatchers.Main.BeginInvoke(() =>
{
SelectFileWindow selectFileWindow = new SelectFileWindow();
selectFileWindow.Closed += new EventHandler(selectFileWindow_Closed);
selectFileWindow.Show();
});
}
The following method is called when the window is closed:
void selectFileWindow_Closed(object sender, EventArgs e)
{
SelectFileWindow selectFileWindow = (SelectFileWindow)sender;
// Continue if they hit the OK button AND they selected a file
if (selectFileWindow.DialogResult == true && (selectFileWindow.FileInfoFile != null))
{
Dispatchers.Main.BeginInvoke(() =>
{
UploadFile(selectFileWindow.FileInfoFile);
});
}
}
This code determines where the .ashx upload file handler is and starts the upload:
#region UploadFile
private void UploadFile(FileInfo SelectedFile)
{
// If there is a file upload it
if (SelectedFile != null)
{
// Get the upload URL
string strURLWithSelectedFolder = string.Format("{0}{1}", GetBaseAddress(), "FileUpload.ashx");
Uri uri = new Uri(strURLWithSelectedFolder, UriKind.Absolute);
UploadUrl = uri;
// Create an FileUpload object
SimpleMVVMFileUpload.FileUpload upload =
new SimpleMVVMFileUpload.FileUpload(this.DataWorkspace.Details.Dispatcher, UploadUrl, SelectedFile);
upload.ChunkSize = 4194304;
// Wire up handles for status changed and upload percentage
// These will be updating the properties that the ViewModel exposes
upload.StatusChanged += new EventHandler(upload_StatusChanged);
upload.UploadProgressChanged +=
new SimpleMVVMFileUpload.ProgressChangedEvent(upload_UploadProgressChanged);
// Start the Upload
upload.Upload();
}
}
#endregion
(note: for a full explanation of the file upload code, see this article: Silverlight View Model Style File Manager Drag and Drop Upload)
When the file has completed uploading, the file list is refreshed:
#region upload_StatusChanged
void upload_StatusChanged(object sender, EventArgs e)
{
SimpleMVVMFileUpload.FileUpload fu = sender as SimpleMVVMFileUpload.FileUpload;
// Is upload complete?
if (fu.Status == SimpleMVVMFileUpload.FileUploadStatus.Complete)
{
this.FileRecords.Refresh();
}
}
#endregion
Downloading Files
For downloading files, we first look at the CanExecute code that disables the button if no file is selected:
#region DownloadFile_CanExecute
partial void DownloadFile_CanExecute(ref bool result)
{
// Do not allow download if no file selected
result = (this.FileRecords.SelectedItem != null);
}
#endregion
Next, we look at the Execute code simply calls Download.aspx file, passing the name of the file to download:
#region DownloadFile_Execute
partial void DownloadFile_Execute()
{
Dispatchers.Main.Invoke(() =>
{
HtmlPage.Window.Navigate(new Uri(string.Format("{0}",
String.Format(@"DownloadFile.aspx?FileName={0}",
this.FileRecords.SelectedItem.FileName)), UriKind.Relative), "_new");
});
}
#endregion
The following is the code for DownloadFile.aspx:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
namespace LightSwitchApplication.FileManager
{
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Get parameters
string FileName = Request.QueryString["FileName"];
// All parameters must have a value or return nothing
if (
FileName != ""
)
{
try
{
string path = MapPath(@"~\");
// Find instance of "bin"
int intPositionOfBin = path.ToLower().IndexOf("bin");
if (intPositionOfBin > -1)
{
path = path.Substring(0, intPositionOfBin);
}
path = path + @"Files\";
// Remove any .. to prevent backing up directories
FileName = FileName.Replace("..", "");
string strPath = Path.Combine(path, FileName);
// Return the file
Response.ClearHeaders();
Response.AddHeader("content-disposition", string.Format("attachment; filename={0}", FileName));
Response.ClearContent();
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.ContentType = GetContentType(strPath);
FileStream sourceFile = new FileStream(strPath, FileMode.Open);
long FileSize;
FileSize = sourceFile.Length;
byte[] getContent = new byte[(int)FileSize];
sourceFile.Read(getContent, 0, (int)sourceFile.Length);
sourceFile.Close();
Response.BinaryWrite(getContent);
Response.Flush();
Response.Close();
}
catch (Exception ex)
{
lblDisplayFilesError.Text = ex.Message;
}
}
}
#region GetContentType
public string GetContentType(string strextension)
{
string contentType;
switch (strextension.ToLower())
{
case ".gif":
contentType = "image/gif";
break;
case ".jpg":
case ".jpeg":
contentType = "image/jpeg";
break;
case ".png":
contentType = "image/png";
break;
case ".doc":
contentType = "application/ms-word";
break;
case ".docx":
contentType = "application/vnd.ms-word.document.12";
break;
case ".pdf":
contentType = "application/pdf";
break;
case ".xls":
contentType = "application/vnd.ms-excel";
break;
case ".ppt":
contentType = "application/vnd.ms-powerpoint";
break;
case ".zip":
contentType = "application/zip";
break;
case ".txt":
contentType = "text/plain";
break;
default:
contentType = "application/octet-stream";
break;
}
return contentType;
}
#endregion
}
}
Download Code
The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx
(note: When you deploy the application, you must give permission to the web server process to access the files on the file system)
55 comment(s) so far...
This was helpful
By Visitor on
7/12/2011 3:16 PM
|
This is nice.
But how do you do it with a "Out of browser" app?
This should be easier (or?)
Regards Sven
By Sven on
8/4/2011 4:15 AM
|
@Sven Sorry I don't have any examples
By Michael Washington on
8/4/2011 4:15 AM
|
Does the project have to be setup with C# or VB ?
By David on
8/18/2011 8:28 PM
|
Good Example. Would you know of an example of uploading multiple files into database ?
By Kris on
9/8/2011 3:12 PM
|
@Kris - See the link How Do I: Import and Store a Data File by Matt Sampson in the article.
By Michael Washington on
9/8/2011 3:13 PM
|
Yes - Thats an excellent article from Matt. Although I believe that is one file at a time. What i would like to do is select many in the dialog and import them using FileInfo[] async. I will keep digging around though. Thanks Michael
By Kris on
9/9/2011 4:15 AM
|
Nice example, working great. I have a question about the downloading part.
Is it possible to calling the DownloadFile.aspx with an id and then fetching a binary from the database and downloading this binary the same way. How do you reference the database serverside?
By Fred on
9/9/2011 5:59 AM
|
@Fred - It may be possible but I have no examples.
By Michael Washington on
9/9/2011 7:18 AM
|
thanks for the sample, I'm about to play with this but curious... when adding this to an existing project do I manually add those files and directories to the ServerGenerated project and edit the project file to include the .ascx and .ashx files?
By nwillis on
9/22/2011 2:40 PM
|
@nwillis - Yes you manually add the files.
By Michael Washington on
9/22/2011 2:52 PM
|
thanks for the reply. I was curious, is there any way to access the files uploaded from outside the lightswitch application. Maybe via url link?
By nwillis on
9/22/2011 4:28 PM
|
@nwillis - Yes but I have no code samples.
By Michael Washington on
9/22/2011 5:32 PM
|
the sample project works as is in debug mode but I cannot connect to the RIA service after I publish it: "Unable to load data. Please check your network connections and try loading again" in the publish menu there's nothing specified for the FileDomainServiceData connection. Do I need to enter some kind of connection string for it to work after getting published? THANKS
By nwillis on
9/26/2011 3:37 PM
|
@nwillis - Please post any questions to the forums. This Blog comments section is a hard place to have a discussion.
By Michael Washington on
9/26/2011 3:45 PM
|
Trying to use this in a project. Getting " 'SimpleMVVMFileUpload.FileUpload' is not defined " in the screen code.
Must be missing something -- Any thoughts?
I added the files and the build section in the properties file of the project. Otherwise everything is compiling.
By DA GAMER on
12/18/2011 3:08 PM
|
@DA GAMER - Post any questions to the forums on this site.
By Michael Washington on
12/18/2011 3:22 PM
|
is it possible to save files to hard drive with wcf ria service ?
By Şükrü Tulga on
2/15/2012 5:17 AM
|
@Şükrü Tulga - Yes but I have no examples.
By Michael Washington on
2/15/2012 5:17 AM
|
When we try to upload files of 3MB size we obtain exceptions. Can I solve this problem?
By acampo on
2/16/2012 4:03 AM
|
Hi Michael,
how can I put a new Button on this "SelectFileDialog" which allows to add new data (one or many rows) in my LightSwitch Business Screen?
By John_C on
4/23/2012 5:07 AM
|
Hi Michael,
how can I put a new Button on this "SelectFileDialog" which allows to add new data (one or many rows) in my LightSwitch Business Screen?
By John_C on
4/26/2012 8:04 PM
|
do you have a tutorial about on how to delete files on the file menu of the wcf?
By babykian on
4/26/2012 7:39 PM
|
@babykian - See http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/86/Help-Desk-An-Advanced-Visual-Studio-LightSwitch-Application.aspx for an example of deleting files
By Michael Washington on
4/26/2012 8:05 PM
|
Hey Guys,
you application works perfectly fine on my local machine. But when I upload it to my server it gives throws error when I try to upload an .xls file
This what I have in my Event Viewer
Event code: 4011 Event message: An unhandled access exception has occurred. Event time: 31/07/2012 12:03:29 Event time (UTC): 31/07/2012 11:03:29 Event ID: d8317057e02b404b9cee0a89072d2521 Event sequence: 12 Event occurrence: 1 Event detail code: 0 Application information: Application domain: /LM/W3SVC/1/ROOT/LSFileManager-4-129882061975689515 Trust level: Full Application Virtual Path: /LSFileManager Application Path: C:\inetpub\wwwroot\LSFileManager\ Machine name: QHSW21912 Process information: Process ID: 6180 Process name: w3wp.exe Account name: IIS APPPOOL\ASP.NET v4.0 Request information: Request URL: http://localhost/LSFileManager/FileUpload.ashx?filename=ExcelSheet.xls&StartByte=0&Complete=True Request path: /LSFileManager/FileUpload.ashx User host address: ::1 User: Is authenticated: False
Thank you, Sanjay Hatwal
By HatSoft on
7/31/2012 3:06 AM
|
@HatSoft - You have to give create and write permission to "IIS APPPOOL\ASP.NET v4.0" on your server.
By Michael Washington on
9/3/2012 10:39 AM
|
I am not able to see the Server Generated folder so I can view the Download.aspx file. Did I miss a step?
By Josh Calhoun on
9/11/2012 7:38 AM
|
@Josh Calhoun - Sorry this tutorial is old. For Visual Studio 2012, the folder you now want is the "Server" folder.
By Michael Washington on
9/11/2012 8:36 AM
|
@ Michael
Thanks for your help.
However I do have one more question. Where do we designate the destination directory? Do we place it here in the code below?
string strFileDirectory = HostingEnvironment.MapPath(@"~\");
Thanks Michael. Keep up the good work with the tutorials.
By Josh Calhoun on
9/13/2012 4:41 AM
|
@Michael- thanks for your help.
By Josh Calhoun on
9/13/2012 4:04 AM
|
@Josh Calhoun - Yes that is where you set the destination directory.
By Michael Washington on
9/13/2012 5:01 AM
|
Thanks Michael, sorry to bother you again about this but I am running into another issue.
I posted a question about the RIA Service connection on the forums below.
http://lightswitchhelpwebsite.com/Forum/tabid/63/aft/889/Default.aspx
Do I need to add a connection string to my RIA service for this to work? I have more details at the location above.
again, Thanks for your help.
By Josh Calhoun on
9/14/2012 6:23 AM
|
@Josh Calhoun - I can only suggest that you download the example project and compare it to your own.
By Michael Washington on
9/14/2012 7:07 AM
|
Hi, is it possible to do this but when app is set as Desktop Application? Thanks
By Naum on
9/26/2012 5:45 AM
|
@Naum - It may be possible but I have no examples, sorry.
By Michael Washington on
9/26/2012 10:15 AM
|
Hi, I'm looking for a way to save an uploaded file on a dynamic directory depending on a parameter. How can I achieve this?. Thanks for you help.
By Jorge on
11/12/2012 12:08 PM
|
Hi Michael,
Just want to ask if it is possible to put the Download.aspx and UploadHandler.ashx in a dll. Then referenced that dll in the Server Project? And how would I tell Lightswitch to included those two files.
Thanks
By Gabriel on
11/15/2012 5:12 AM
|
Just got an error on submitting a comment, so sorry if this is a repost.
I would love to see "file upload" as a LightSwitch field control, so that we could simply go to a text field in the designer, click "file upload" as a control type, then select the default folder. This way any application could use file upload.
I also would love to see the same thing with Telerik's RichTextBox (http://www.telerik.com/products/silverlight/resources/lightswitch-support.aspx). Reusable seems to be the logical next up. Thoughts?
Thanks, Jason
By Jason on
11/16/2012 9:25 AM
|
I have converted my solution Lightswitch in VisualStudio 2012. Now the WCF RIA Service is ok but the download and the upload aren't work. Maybe the problem is that in Lightswitch 2012 haven't the section ServerGenerated in file view. I hope to have your help thanks
By alessio6181 on
3/5/2013 1:26 PM
|
@alessio6181 - I don't know when/if I will get time to update this. I have over 80 samples so when they get out dated I am unable to update them, sorry.
By Michael Washington on
3/5/2013 2:35 PM
|
Regarding alessio's comment, I converted to 2012, and the solution worked. Regarding 'ServerGenerated' in file view, check under 'Server' now.
Michael, any quick advice on handling this with larger files? Works fine for .jpg and other smaller files, but I'm getting exception "The remote server returned an error: NotFound." in the ReadCallback method (line HttpWebResponse response = (HttpWebResponse)webrequest.EndGetResponse(asynchronousResult);). Also, thanks for the great learning resources. They are excellent. Greatly appreciated.
By ryan on
3/23/2013 2:25 PM
|
@ryan - Sorry no.
By Michael Washington on
3/23/2013 2:36 PM
|
No worries, I found the "problem"... and it was really no problem. ;-) Just related to the file type and what was being validated in CheckFileExtension. Just had to add additional file types to this method and it works spot on. Thank you sir
By ryan on
3/23/2013 8:28 PM
|
Hi Michael,
I've practiced this solution in debug, and it works great. Now, after deploying this to IIS, I'm getting "The resource cannot be found." when trying to use DownloadFile.aspx and FileUpload.ashx. I edited the lsproj file to include these files. I validated the files exist on the web server. Any suggestions?
Greatly appreciated, sir.
By ryan on
4/8/2013 8:18 AM
|
@ryan - Sorry I'm stumped :(
By Michael Washington on
4/8/2013 8:19 AM
|
Hi Michael, Thanks for sharing this example. I was wondering if there was anyway to rename the files as part of the upload process?
By Stewart Fisher on
8/21/2013 5:53 AM
|
@Stewart Fisher - Yes but I have no examples.
By Michael Washington on
8/21/2013 5:53 AM
|
Noob question: Its the same for Lightswitch in the VS2013 Version? Thanks.
By NESTicle on
9/23/2013 12:59 PM
|
@NESTicle - It should work but has never been tested on VS2013
By Michael Washington on
9/23/2013 2:03 PM
|
Michael,
I managed to convert the solution to VS2013, but when I try to upload the file an error is fired in the following event :
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { long lengthtemp = 0; if (!string.IsNullOrEmpty(e.Result)) { lengthtemp = long.Parse(e.Result); } ....
The error is a "TargetInvocationException" that happens when trying to long.Parse the e.Result.
The InnerException message is: "The remote server returned an error: NotFound."
The whole thing shows that "WebClient.DownloadStringAsync" does not work as expected. Let me note that under VS2012 everything works fine.
Thanks in advance for any possible idea or solution.
By Themis on
12/20/2013 8:26 AM
|
I just solved my VS2013 problem.
For VS2013 just change the string that searches app root path in GetBaseAddress() method.
...IndexOf(@"/desktopclient/default");
private static Uri GetBaseAddress() { // Get the web address of the .xap that launched this application string strBaseWebAddress = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsoluteUri; // Find the position of the ClientBin directory int PositionOfClientBin = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsoluteUri.ToLower().IndexOf(@"/desktopclient/default");
By Themis on
12/20/2013 8:26 AM
|
Michael, I went through the article by matt and created the Silverlight control to save a file. My Issue Tracker software is a desktop app. The problem I ran into was that I need the file information file to be owned by an upper level file and can't make it behave correctly. I guess my question is can I combine your Web application approach here with my desktop app? Thanks for any advice you might offer me.
By Vern on
2/4/2014 4:52 PM
|
@Vern - You can set "string path" to be anywhere you need. However, I am sorry it has been years since I have done any Silverlight development and I have no plans to return to it.
By Michael Washington on
2/4/2014 6:26 PM
|
@Michael - In regards to the exception posted by HatSoft, "User does not have access to the invoked operation". I have LightSwitch 2012 app running in IIS for a long time, no errors. However, I just started getting this exception after I deployed to a Windows 8.1 box yesterday. It occurs when I go to upload a file. It is thrown before the actual file system is accessed. I dont think it is related at all to this file upload solution, because I have a windows service also consuming the lightswitch data, and this service is throwing the same error when pointed to the new deployment. It seems to occur when consuming the LightSwitch data, calling ApplicationDataService(). Any advice you can lend?
Much appreciated, Ryan
By Ryan on
9/30/2014 1:46 PM
|
@Ryan - Sorry I have not experienced the error you are getting at all :(
By Michael Washington on
9/30/2014 1:47 PM
|