FileUpload within a Wizard Control, Processed at the End - asp.net

This is related to my previous question. An unforeseen issue arose with the Wizard control.
I now know how to upload to FTP, however when using the FileUpload control inside a Wizard control, when you move to the next step, the File you selected gets cleared because of the postback. I need to be able to rename the file according to the results from the Wizard before uploading. So...
I finish my wizard
It uploads some stuff to a database
Renames the file according to those results
Uploads the renamed file to the FTP server
I suspect I will need to follow a procedure something like this, having an upload button next to FileUpload
On "Upload" button click stream the file to the Web Server.
Complete the Wizard.
If the wizard completes successfully, rename file and stream to FTP server.
If the wizard fails, what? Delete the file from the web server? How?
I think I understand the process, so I would like help on how to split my FTP Upload function into two parts with the proper error handling for when the wizard fails.
It would be a great help if you please use the following code as a base. Thanks as always :)
Protected Sub UploadFile(ByVal NewFilename As String)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
'Function one? - Problem, "NewFilename" depends on the output of the Wizard,
' but obviously it has not been called yet.
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + NewFilename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
Dim myFileStream As Stream = FileUpload1.FileContent
myFtpWebRequest.ContentLength = myFileStream.Length
'Function two?
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub
-- ANSWER ---
Here's my final implementation based on input from Icarus :)
For brevity I have excluded the error catching.
'This function is what kicks things off...
Protected Sub UploadFileToWebServer() Handles btnUploadFile.Click
Dim TempDir As String = "C:\TEMP", FileName As String = "uploadedfile.tmp", FilePath As String
If Not Directory.Exists(TempDir) Then
Directory.CreateDirectory(TempDir).Attributes = FileAttributes.Directory
End If
FilePath = TempDir + "\" + FileName
Session.Add("FileName", File1.FileName) 'Keep track of uploaded file name
File1.SaveAs(FilePath)
Session.Add("File", FilePath)
End Sub
After the file is uploaded to the web server, we can continue through the wizard, and when the "Finish" button is clicked, the wizard data gets submitted to the database. The filename is based on the inserted record ID. The following function gets called by the "Final" button click after the record is inserted, and the file finally gets uploaded to the FTP server with the filename changed accordingly.
Protected Sub UploadFileToFtpServer(ByVal FileLinkStr As String)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
'Defines the filename, path, and upload method, and connection credentials
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + FileLinkStr), FtpWebRequest)
'Be sure to authenticate prior to uploading or nothing will upload and no error
myFtpWebRequest.Credentials = New NetworkCredential(ftpUsername, ftpPassword)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
'Streams the file to the FTP server
'Retrieves File temporarily uploaded to the Web Server during Wizard Processing
Dim iStream As New FileInfo(Session.Item("File"))
Dim myFileStream As Stream = iStream.OpenRead
myFtpWebRequest.ContentLength = myFileStream.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub

Your understanding is correct. Once you upload the file to the web server (you'd need to place it in a temp directory somewhere and keep track of the file name you gave it) and the wizard completes successfully, you grab that file, rename it accordingly and upload it to the ftp server. If fails, simply call:
File.Delete(Path_to_file_uploaded_on_temp_directory);
You can keep track of the file name given originally, by storing it in Session, for example. When you upload the file to the server initially, do something like Session["FileName"]=Path_to_temp_directory+fileName;
On the final step of the Wizard, get the file name from Session and either rename it and upload it to the FTP Server or delete it.
Of course you need to account for possible name conflicts, etc. You can use a Guid to generate a random name for the file, for example.
I hope I explained this clearly.
EDIT
To make sure I understand correctly...
You need your user to go through all the steps of a Wizard kind of thing
During the process, you ask your user to upload a file.
Because the user has to select a file before the last step of the wizard, you are forced to upload the file immediately the user clicks on the "Next" button to go to the next step of the wizard.
At the very last step of the Wizard, you need to determine whether the file the user has selected should be uploaded to an ftp server (presumably, another box different from your web server) or should be discarded completely.
If the file needs to be uploaded to the FTP server, it needs to be renamed with a special name.
Based on the above, my suggestion is:
When the user clicks "Next" on the step where he selects the file from his computer, you need to save the file immediately to a temporary location on your web server. You save the file to this temporary folder on your web server by doing something like:
if(FileUpload1.HasFile) //user selected a file
{
try
{
//D:\temp is a temp directory on the Web Server
FileUpload1.PostedFile.SaveAs(#"D:\temp\"+FileUpload1.FileName);
//Store the FULL PATH TO the file just uploaded on Session
Session["FileName"]="D:\temp\"+FileUpload1.FileName;
}
catch (Exception ex)
{
//Handle it.
}
}
On the last step of the wizard, assuming everything was successful, do this
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
' You know the NewFileName because it's the output of the wizard
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + NewFilename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
'Here you need to read the Original File
Dim myFileStream As Stream = new FileStream(Session["FileName"]),FileMode.Open,FileAccess.Read,FileShare.ReadWrite)
myFtpWebRequest.ContentLength = myFileStream.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
If you decide that you should delete the original file uploaded by the user because he did not complete the wizard successfully, you can simply do:
try
{
File.Delete (Session["FileName"]);
}
catch(Exception ex)
{
//Handle it.
}

Related

Azure Asp.net download files from DropBox using Dropbox.Api

My asp.net web app requires downloading files from DropBox.
In DropBox, I created an app and have the API key and Auth Token, Secret, etc.
In my web app I have reference to Dropbox.Api and can create DropboxClient.
Like so:
Dim myDBClient As New DropboxClient("my_token_auth")
What do I do next? Does anyone have sample code.
I read I need to call async method to download such as code below.
But the code is not working. Not failing either but does nothing.
Any help would be appreciated. Thank you
Dim folder As String = "C:\Data"
Dim file2 As String = "myFile.txt"
Using response = myDBClient.Files.DownloadAsync("/" & folder & "/" & file2)
Using fileStream = File.Create("C:\Data\myFile.txt")
(response.GetContentAsStreamAsync()).CopyTo(fileStream))
End Using
End Using
You need to use Await to get the expected result.
Demo code:
Dim myDbClient As New DropboxClient("auth token")
Dim folder = "test" 'dropbox folder name
Dim fileName = "1.txt" 'dropbox file name
Dim response As IDownloadResponse(Of FileMetadata) = Await myDbClient.Files.DownloadAsync("/" + folder + "/" + fileName)
Dim bytes = Await response.GetContentAsByteArrayAsync()
Using fileStream = Create("D:\tom\2.txt") ' local path
fileStream.Write(bytes, 0, bytes.Length)
End Using
Update:
Add the detail test steps:
1.Create dropbox app and generate the auth token.
2.Create the folder and upload the file to dropbox folder
3.Create an Asp.net empty project and add a webform.aspx file named DropBoxApiTest.aspx and set it as start page.
4.To install Dropbox.Api, run the following command in the Package Manager Console:
PM> Install-Package Dropbox.Api
5.Add the download button in the Webpage
<asp:Button ID="_btnDownload" runat="server" Text="Download" />
6.Add the click event for the button and the following code.
Protected Async Sub _btnDownload_Click(sender As Object, e As EventArgs) Handles _btnDownload.Click
Dim token = "xaMln4bUnRAAAAAAAAAAIq...."
Dim myDbClient As New DropboxClient(token)
Dim folder = "test" 'dropbox folder name
Dim fileName = "1.txt" 'dropbox file name
Dim response As IDownloadResponse(Of FileMetadata) = Await myDbClient.Files.DownloadAsync("/" + folder + "/" + fileName)
Dim bytes = Await response.GetContentAsByteArrayAsync()
Using fileStream = Create("D:\tom\download.txt") ' local path
fileStream.Write(bytes, 0, bytes.Length)
End Using
End Sub
7.We also need to change aspx Async to true
8.Test it locally.

Saving Files to user Computer in VB.Net

I have look for days, with no luck.
Can anyone tell me how to save files in VB.Net to any computer? There are lost of articles, but those only tell you about saving to your own computer by giving folder access, not a random user's computer.
You can see my example here http://hanontest.com/POShellCreator.aspx (You have to enter text into task code, project id and notes field, then click create. Then click export, you will see the error.)
I can go to a local Pizza shop website and download a menu pdf, I know its possible.
In my example it saves when you click the button, I would like a save as dialog if anyone knows how to do that as well.
Here is the save string:
Dim regDate As Date = Date.Now()
Dim strDate As String = regDate.ToString(".yyyy\.MM\.dd")
TextBox5.Text = "c:\temp\" & Vendor & "&" & Vendor2 & "&" & Vendor3 & "&" & Vendor4 & TaskEmpty & strDate & ".csv"
Here is how I am saving:
Public Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'Define Path to save file.
Dim path As String = TextBox5.Text
' Create or overwrite the file.
Dim fs As FileStream = File.Create(path)
' Add text to the file.
Dim info As Byte() = New UTF8Encoding(True).GetBytes(TextBox3.Text)
fs.Write(info, 0, info.Length)
fs.Close()
End Sub
The error you're seeing there is because you're attempting to save the text file to the server's
file system, which you don't have write access to.
To return a file to user from the server you need to do a little more work, and it's not going to simple on a postback from a button.
You need to send the data down to the client in the Response.OutputStream, instead of your page, and also tell the browser to treat it as a file download:
Response.ContentType = "text/plain";
Response.AppendHeader("Content-Disposition", "attachment; filename=textFile.csv");

Error happens during file open in asp.net on wep page?

I try to open the file and update content in the file, it gets sum error like this.
My code
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If Request.QueryString("log") = "no" Then
pinfo.Text = "Invalid Username / Password"
End If
Session.Clear()
Dim FileWriter As StreamWriter
Dim FileReader As StreamReader
Dim Countstr As String
FileReader = File.OpenText("/dmkg/Counter.txt")
Countstr = FileReader.ReadLine
FileReader.Close()
Countstr = Countstr + 700
FileWriter = File.CreateText("/dmkg/Counter.txt")
FileWriter.WriteLine(Countstr)
FileWriter.Close()
End Sub
Check the code and tell me where I am going wrong.
You have to Pass Correct PATH in OpenText Method not File Name
File.OpenText()
Eg:
Dim path As String = "c:\temp\MyTest.txt"
FileReader = File.OpenText(path)
So Make sure your file path is correct.
If it's stored in server then you need to use Server.Mappath()
FileReader = File.OpenText(Server.MapPath("/dmkg/Counter.txt"))
Server.MapPath
To Enable a Access
To grant ASP.NET write access to a path,
right-click the file in Explorer,
choose "Properties" and select the Security tab.
Click "Add" to add the appropriate user or group (typically
{MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6).
Highlight the needed account, and check the boxes for the desired
access.

Can't share isolated storage file between applications in different app pools

I've got various web apps (containing WCF services) in IIS under the default website. As long as they are all running in the same app pool they can access a shared isolated storage file no problem.
However, once I move them to different app pools I get "System.IO.IsolatedStorage.IsolatedStorageException: Unable to create mutex" when one tries to access a file created by another. They are all running under NetworkService user. I tried GetUserStoreForAssembly and GetMachineStoreForAssembly all with the same result. Any ideas why they couldn't use a shared file?
I made sure to close the stream and even dispose it in case one was holding onto it, but I am running a simple test where one service writes it, then another tries to read from it later, and it always fails.
Also, I am accessing the isolated store from a signed assembly.
Does anybody have any ideas?
Here is the code:
Private Sub LoadData()
Dim filename = FullFilePath(_fileName)
Dim isoStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
isoStorage.CreateDirectory(ROOT_DIRECTORY)
If (isoStorage.GetFileNames(filename).Length = 0) Then
Return
End If
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, isoStorage)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
Dim appData As Hashtable = DirectCast(formatter.Deserialize(stream), Hashtable)
Dim enumerator As IDictionaryEnumerator = appData.GetEnumerator()
While enumerator.MoveNext()
Me(enumerator.Key) = enumerator.Value
End While
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Public Sub Save()
Dim filename = FullFilePath(_fileName)
' Open the stream from the IsolatedStorage.
Dim isoFile As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.Create, isoFile)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
formatter.Serialize(stream, DirectCast(Me, Hashtable))
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Looks like it was a trust issue.
After adding the assembly accessing the isolated storage file to the gac it magically worked as everything in the gac has full trust set automatically.
This works for me, but it might not always be an option to do this for other solutions. Check out the .NET Framework caspol utility if this is the case.
Hope this helps somebody! It was a huge pitafor me.

How to make a fileupload interface in ASP.NET

I am trying to make a file upload interface in ASP.NET webforms and am looking for some advice on how to proceed.
The file upload interface is part of a website I am making on which users can post adverts. The interface is part of the "create a new advert" and will enable the user to upload up to 6 images. I am using only the asp.net FileUpload server control as I am trying to make a control which will work when users have javascript disabled. That's the background.
The upload for all 6 files occurs on button click. This stores the files in a temp folder (/UserUploads/temp) until the user submits the form in which case the files are moved to the /UserUploads folder and the references in the database or until the user hits the cancel button or navigates away in which case the files are deleted.
First question is: Is storing the files in a temp directory the right way to go about this? Or is there some better way of keeping the temp files on the server until the parent form is submitted? The only alternative I can think about is saving the files to the session, but that seems like a recipe for killing the server...
Second question: I am unclear what to do when the user just closes the browser window. I want to avoid ending up with a mess of orphaned files in the temp directory. Is there some way to make sure that all the files will get cleared out if the user doesn't go through with the form submission? Or do I just have to perform a cleanup of the temp directory every so often?
Third question: Am I doing this completely wrong and there is in fact a much better approach to uploading multiple files?
1) If you are using SQL Server, I personally prefer to store uploaded files in a varbinary(max) field and work with them by their unique ID. Then you don't have to worry about name collisions or de-sync of your DB to your filesystem. This also allows your upload process to be independent of the insertion of the parent form.
The examples below show how to grab the file stream (and metadata) from a FileUpload control in a FormView and supply it as a parameter to a SQL stored procedure. Then, a class implementing IHTTPHandler is used to retrieve files from the DB.
2) As far as clearing out temp files, I would associate each uploaded file with a temp master record so they are tied together. When the real master is confirmed, delete the temp master (and reference files from the real master). Then run a SQL Agent job on a regular interval to delete temp masters and associated files that are older than X amount of time.
Saving:
Protected Sub DetailsView1_ItemInserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewInsertEventArgs) Handles DetailsView1.ItemInserting
Dim objUploader As FileUpload = DetailsView1.FindControl("fuFile")
If objUploader.HasFile Then
Dim strFileName As String = objUploader.PostedFile.FileName
strFileName = strFileName.Substring(strFileName.LastIndexOf("\") + 1)
Dim objFileStream As System.IO.Stream = objUploader.PostedFile.InputStream
Dim arrFileImageByteArray(objFileStream.Length) As Byte
objFileStream.Read(arrFileImageByteArray, 0, objFileStream.Length)
e.Values.Insert(0, "FileImage", arrFileImageByteArray)
e.Values.Insert(1, "FileName", strFileName)
e.Values.Insert(3, "PostingDate", Now)
e.Values.Insert(5, "Application", "CorpForms")
Else
e.Cancel = True
objMessages.Add(New StatusMessage(MessageType.Warning, "File Upload canceled. No file was selected."))
End If
End Sub
Retrieving:
Public Class FileServiceHandler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim idFileID As Guid
If context.Request.QueryString("FileID") IsNot Nothing Then
Dim strFileID As String = context.Request.QueryString("FileID")
Try
idFileID = Guid.Parse(strFileID)
Catch ex As Exception
Throw New Exception("Unable to parse File ID")
End Try
End If
Dim objConnection As New SqlConnection(ConfigurationManager.ConnectionStrings("PublicWebConnectionString").ConnectionString)
Dim objCommand As SqlCommand = objConnection.CreateCommand
Dim objReader As SqlDataReader
objCommand.CommandType = Data.CommandType.StoredProcedure
objCommand.CommandText = "spGetUploadedFile"
objCommand.Parameters.AddWithValue("FileID", idFileID.ToString)
Dim arrFileImage() As Byte = Nothing
Dim strFileName As String = String.Empty
Try
objConnection.Open()
objReader = objCommand.ExecuteReader
While objReader.Read
If Not IsDBNull(objReader("FileImage")) Then
arrFileImage = objReader("FileImage")
End If
If Not IsDBNull(objReader("FileName")) Then
strFileName = objReader("FileName")
End If
End While
Catch ex As Exception
Throw New Exception("There was a problem retreiving the file: " & ex.Message)
End Try
If objConnection.State <> Data.ConnectionState.Closed Then
objConnection.Close()
End If
If arrFileImage IsNot Nothing Then
context.Response.Clear()
context.Response.AddHeader("content-disposition", "attachment;filename=" & strFileName)
context.Response.BinaryWrite(arrFileImage)
context.Response.End()
Else
context.Response.ContentType = "text/plain"
context.Response.Write("Unable to retrieve file ID# " & idFileID.ToString)
End If
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Web.Config in file retrieval path:
<configuration>
<system.web>
<httpHandlers>
<add verb="GET" path="*" type="MyNamespace.FileServiceHandler" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add name="MyNamespace.FileServiceHandler" path="*" verb="*" type="MyNamespace.FileServiceHandler" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>
</configuration>
File upload is always annoying. I recently found a great component that does what I belive all upload componentes should do.
See at:
http://aquantum-demo.appspot.com/file-upload
And a sample in C# at:
https://github.com/BoSchatzberg/jfu-CSharp-Example
And, you should store your files in a temporary folder before creating the database row. To avoid a mess of files left useless, you can use a windows temporary folder to delagate to the windows when delete or not those files.
System.IO.Path.GetTempPath()
I would recommend storing the files in the database, rather than a temporary folder.
Don't store them in Session Db - too much information, but include the SessionId in the Files Database record.
So you'd have a Files database with a table along the lines of
Id (int identity field)
Data (varbinary(max))
MimeType (varchar(50))
SessionId (varchar(50))
UserId ??
Then you'd simply need to write a scheduled SQL task to clear images where the session had expired.

Resources