I've been reading gobs of articles on FTP upload in ASP.NET recently and they all seem to make sense, but every time I've tried implementing them I either get an empty file uploaded, or no file at all. Here are some of the articles I've been reading:
Managing FTP Transfers from an ASP.NET Web Page By John Peterson
FileUpload Control Doesn’t Give Full Path….HELP!!!!
How to: Upload Files with the FileUpload Web Server Control
They're all great articles, but like I said, having issues :(
I know exactly what the problem is but I don't know how to fix it. I can pass the file name from the FileUpload control, but the path does not exist for security concerns. However, the StreamReader object requires the fully qualified path of the file to be uploaded, so how the heck do I get that? I'm at my wits end! >.<
Let's use the example by John Peterson that I linked above. Here's the code:
Protected Sub btnUploadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
Dim myStreamWriter As StreamWriter
myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
myStreamWriter = New StreamWriter(myFtpWebRequest.GetRequestStream())
'IT BREAKS HERE BECAUSE THE CLIENT PATH IS WRONG!!
myStreamWriter.Write(New StreamReader(Server.MapPath("filename.ext")).ReadToEnd)
myStreamWriter.Close()
myFtpWebResponse = myFtpWebRequest.GetResponse()
myFtpWebResponse.Close()
End Sub
See? No data in the uploaded file :(
Now my latest implementation looks like this, but the uploaded file is much larger than the source, and corrupted. Seriously, what the heck am I doing wrong? I've been two LONG days at this, grrr...
Protected Sub btnUploadFile2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
filename = Path.GetFileName(FileUpload1.FileName)
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + filename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
'NEW APPROACH USING THE STREAM OF THE FILE FROM THE FileUpload Control
'CORRECT BYTE LENGTH - in sourceStream.BaseStream
Dim sourceStream As New StreamReader(FileUpload1.FileContent)
'WRONG BYTE LENGTH - in sourceStream.ReadToEnd()
Dim fileContents As Byte() = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd())
sourceStream.Close()
myFtpWebRequest.ContentLength = fileContents.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
requestStream.Write(fileContents, 0, fileContents.Length)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub
Thanks ever so much to Adam Maras for the amazing answer. I'll leave my blunders here for others to benefit who find this thread ;)
First of all, you must upload through the web server if you're going to use ASP.NET like this. Without installing a plugin on the client's browser or using an ActiveX control (or similar) you absolutely cannot upload directly from the client machine to the FTP server.
I assume you're uploading binary files; if that's the case, the way you're using StreamReaders and StreamWriters could be corrupting the binary contents of the file. Instead, we can use the Stream.CopyTo method to move the data verbatim from one stream to the other.
I've modified your method to use this pattern instead:
Protected Sub btnUploadFile2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
filename = Path.GetFileName(FileUpload1.FileName)
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + filename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
Dim myFileStream As Stream = FileUpload1.FileContent
myFtpWebRequest.ContentLength = myFileStream.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub
The FileUpload.SaveAs() method saves to the Web server's local file system, and can't write to a URI or FTP site. To do that, you'll need to create a WebRequest.
See the MSDN reference for the FileUpload control here: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.fileupload.saveas.aspx
and for the FTP use of a WebRequest here: http://msdn.microsoft.com/en-us/library/ms229715.aspx
Note the example given in the FileUpload documentation saves to c:\temp\uploadedfiles. I'd suggest you use Path.GetTempFileName() instead as this is guaranteed to give you a file that can always be written no matter what environment you're under.
The data gets corrupted because you are reading the file as if it was text, but it's not.
Use a BinaryReader instead of a StreamReader so that you can read the data as bytes directly:
Dim fileContents As Byte()
Using sourceStream As New BinaryReader(FileUpload1.FileContent)
fileContents = sourceStream.ReadBytes(Int32.MaxValue)
End Using
Related
I have created an application in VB.NET that is responsible for creating a PDF using iTextSharp. It is hosted on a Windows Web Server with IIS. But sometimes it happens that the version delivered by the server is an old one, it does not show the latest changes made to the structure of the PDF. To solve it, I must upload the version that I have on my PC back to the server. Although I have not made any changes to the source code and it is exactly the same as the one on the server. Every time I invoke the .aspx file I do it with a random value parameter to try to avoid the cache, but apparently it is not the solution. I also have the following statement in the code: Response.Cache.SetCacheability (HttpCacheability.NoCache) Thank you very much for any help you can give me.
Here is how I create PDF: I use a function to create PDF and return it into a MemoryStream :
SendOutPDF(CreatePDF("Title", nro), nro)
Public Function CreatePDF(ByVal Titulo As String, ByVal Numero As Integer) As System.IO.MemoryStream
Dim PDFData As MemoryStream = New MemoryStream
...
Dim pdfDoc As iTextSharp.text.Document
...
Dim pdfWrite As PdfWriter =
iTextSharp.text.pdf.PdfWriter.GetInstance(pdfDoc, PDFData)
...
Return PDFData
End Function
Protected Sub SendOutPDF(ByVal PDFData As System.IO.MemoryStream, ByVal num As Integer)
Response.Clear()
Response.ClearContent()
Response.ClearHeaders()
Response.ContentType = "application/pdf"
Response.Charset = String.Empty
Response.AddHeader("content-disposition", "attachment;filename=" & num & ".pdf")
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.OutputStream.Write(PDFData.GetBuffer, 0, PDFData.GetBuffer.Length)
Response.OutputStream.Flush()
Response.OutputStream.Close()
Response.End()
End Sub
I have a System.Web.UI.WebControls.FileUpload control that passes both Word and PDF files that need to be stored in Azure Blob Storage.
From the Code Behind page it passes to the common library to manage Azure Functions:
Private Sub UploadButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UploadButton.Click
Dim fileExt As String = String.Empty
Dim newGuid As New Guid
Dim fileName As String
Dim documentType As Document.DocumentType
Page.Validate()
newGuid = Guid.NewGuid
If Page.IsValid() AndAlso Me.FileUploadNewDoc.HasFile Then
Try
'Test that MIME is either msword of pdf
If FileUploadNewDoc.PostedFile.ContentType.Contains("msword") Then
fileExt = "doc"
documentType = Document.DocumentType.LeaseWordDoc
ElseIf FileUploadNewDoc.PostedFile.ContentType.Contains("pdf") Then
fileExt = "pdf"
documentType = Document.DocumentType.LeasePDF
Else
fileExt = "na"
End If
If fileExt <> "na" Then
fileName = newGuid.ToString & "." & fileExt
AzureStorage.SaveBlob(FileUploadNewDoc.FileContent, fileName, mDocumentContainer, mStorageConnectionString)
End If
Catch ex As Exception
' Handle Error
Finally
FileUploadNewDoc.Dispose()
End Try
End If
End Sub
The AzureStorage.SaveBlob code:
Public Function SaveBlob(ByRef fileContent As Stream,
ByVal fileName As String,
ByVal containerName As String,
ByVal storageConnectionString As String) As Boolean
Dim storageAccount As CloudStorageAccount = CloudStorageAccount.Parse(storageConnectionString)
Dim blobClient As CloudBlobClient = storageAccount.CreateCloudBlobClient()
Dim container As CloudBlobContainer = blobClient.GetContainerReference(containerName)
Dim blockBlob As CloudBlockBlob = container.GetBlockBlobReference(fileName)
Using fileContent
fileContent.Position = 0
blockBlob.UploadFromStream(fileContent)
End Using
Return True
End Function
My questions:
Is this best way to take the File that has been uploaded and save it to Azure Blob Storage?
Am I handling the Stream correctly? I'm passing ByRef and have a Using statement around the usage.
Should I be setting content type explicitly when saving it to storage? If so how do I do that?
Note, I normally code in C# so an example in C# is fine if you're not familiar with VB.NET.
Is this best way to take the File that has been uploaded and save it
to Azure Blog Storage?
The best way depends on your use case. If it is just small files you're OK. If you want to support large files you might want to do chunked uploading. You can take blocks of 1 megabyte which you can upload separately or in parallel. Once you are done uploading all the blocks you commit the file and it is stiched together in Azure Blob storage. Look at CloudBlockBlob.PutBlock and CloudBlockBlob.PutBlockList.
Am I handling the Stream correctly? I'm passing ByRef and have a Using
statement around the usage.
You are but if you want to support larger files you might want to upload with JavaScript and create two endpoint to receive chunks and to commit after all chunks are sent. There are multiple libraries that can help you.
Should I be setting content type explicitly when saving it to storage? If so
how do I do that?
If you upload files that you want to embed in HTML it's wise to have a content type. If you want the links to the file to be download links you don't have to. Although it can never hurt.
blockBlob.Properties.ContentType = "image/jpeg";
I have tired many things, searched the internet, and still I cannot figure out what is going on with this code. I still get that my docx files are corrupted, but when I do it with doc file everything is going great.
My Upload Code
Private Sub LbReqUploadAttachment1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LbReqUploadAttachment1.Click
If FileUplReqAttachment1.HasFile Then
'Then save the attachment to the documents table
Dim type As String = Me.FileUplReqAttachment1.PostedFile.ContentType
Dim myFile As System.Web.HttpPostedFile = Me.FileUplReqAttachment1.PostedFile
Dim nFileLen As Integer = myFile.ContentLength
Dim myData(nFileLen) As Byte
myFile.InputStream.Read(myData, 0, nFileLen)
Dim DocDto As New DocumentsDto
DocDto.Month = Now.ToString("m")
DocDto.Year = Now.Year
DocDto.MimeType = type
DocDto.UploadedById = MyPage.LoggedOnUser.DtoUser.PersonId
DocDto.DocumentBytes = myData.ToArray
DocDto = MyPage.DelegateDocument.CreateDocumentsDto(DocDto)
'Update the order with the new document id
If Me.TbIntlFlagz.Checked Then
Item.AssetID = CStr(DocDto.DocumentID)
Else
Item.AssetID = "0"
End If
' Save Everything
SaveItem()
'Focus after postback
FileUplReqAttachment1.Focus()
End If
'Stay on order screen
Response.Redirect(String.Format("Default.aspx?i={0}&Item={1}", MyPage.DtoPage.PageID, Me.Item.Id))
End Sub
My download code:
Sub ProcessRequest(ByVal context As HttpContext) Implements ttpHandler.ProcessRequest
Dim docba As Byte() = docDto.DocumentBytes
Dim ext As String = Mime.GetExtensionFromMime(docDto.MimeType)
context.Response.ContentType = docDto.MimeType
If String.IsNullOrEmpty(ext) Then
'We can only use the attachment approach if we found a good extension based on the mime type
Else
Dim DispositionHeader As String
If Not context.Request.QueryString.Item("fn") Is Nothing Then
DispositionHeader = String.Format("attachment; filename={0}.{1}", AntiXss.UrlEncode(context.Request.QueryString.Item("fn")), ext)
Else
DispositionHeader = String.Format("attachment; filename={0}.{1}", AntiXss.UrlEncode("Document"), ext)
End If
context.Response.AppendHeader("Content-Disposition", DispositionHeader)
End If
context.Response.Expires = (60 * 24 * 1)
context.Response.OutputStream.Write(docba, 0, docba.Length)
context.Response.Flush()
docba = Nothing
End If
End Sub
I have tired these with no success:
Why are .docx files being corrupted when downloading from an ASP.NET page?
http://www.aspmessageboard.com/showthread.php?230778-Downloaded-docx-files-are-corrupted
https://social.msdn.microsoft.com/Forums/vstudio/en-US/88383fb2-03c6-49f5-afee-ce38497789bd/retrieving-docx-stored-in-sql-server-results-in-there-was-an-error-opening-the-file?forum=vbgeneral
I am uploading the file into a DB, and downloading the file by pressing a hyperlink. When I press the hyperlink and download the file. View at the file it is corrupted.
In VB, when declaring an array you give it the number of elements in the array. This is different from many languages where you specify the last index of the array.
For the code you show, you need to use
Dim myData(nFileLen - 1) As Byte
to make sure that you do not have an extra element in the array.
It appears that the .doc format is not sensitive to this, but .docx is.
I'm building a web forms app with VSE 2012 and VB that transforms XML (will be uploaded by user) to another XML using XSL and need to allow the user to view/save the transformed doc. I'd like to display the result on screen (as raw XML so it can copied and pated) and offer the ability to download vs. Copy and paste
I've got the transform working but can't figure out how to display as raw XML or give user ability to download. This is what I have so far. It displays the results but only displays contents of nodes, not raw XML:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
Response.ContentType = "text/xml"
'Dim transformedfile As String
Dim xsltFile As String = Path.Combine(Request.PhysicalApplicationPath, _
"Test.xsl")
Dim xmlFile As String = Path.Combine(Request.PhysicalApplicationPath, "XMLDoc.xml")
Dim xslt As New XslCompiledTransform() 'Pass in true to enable XSLT Debugging
xslt.Load(xsltFile)
Dim doc As New XPathDocument(xmlFile)
xslt.Transform(doc, New XmlTextWriter(Response.Output))
Thanks in advance
mark
You're returning a page with content type "text/xml" to the user's browser. (The browser will determine what to do with this page; you can give it hints with a content disposition header of "inline" or "attachment").
This example shows how to do this when returning a PDF file; you're just sending an XML content type, like:
byte[] data = memorystream.ToArray();
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.Buffer = true;
Response.ContentType = "text/xml";
Response.BinaryWrite(data);
Response.End();
I hope this helps.
One of the client requirements is that the server generate files and store them on a special folder. This files created can not be modified by users or deleted.
So the only way I thought is to generate this files with elevated privileges so a normal user can't delete or modify them.
But the question is how can I generate a file with this privileges that normal users can interact with this files... only download from server.
I use this code to generate the file... But I don't know how to configure it for elevated privileges.
This is the button which generate the file and allow to download it:
Protected Sub ibtGenerar_OnClick(ByVal sender As Object, ByVal e As ImageClickEventArgs)
oArchivoTelecredito.NombreArchivo = txtNombreArchivo.Text
oArchivoTelecredito.SesionDetalleArchivosTelecredito = New List(Of DetalleArchivoTelecreditoBE)
Dim oArchivoTelecreditoSL As New ArchivoTelecreditoSL
Response.AddHeader("Content-disposition", "attachment;filename=" & oArchivoTelecredito.NombreArchivo & ".txt")
Response.ContentType = "application/octet-stream"
Response.BinaryWrite(oArchivoTelecreditoSL.GeneraArchivoTelecredito(oArchivoTelecredito, Server.MapPath(oArchivoTelecredito.NombreArchivo)))
Response.End()
End Sub
This is the function which create the file on server:
Public Function GeneraArchivoTelecredito(ByVal telecredito As ArchivoTelecreditoBE, ByVal ruta As String) As Byte()
Dim lineas As Integer = telecredito.SesionDetalleArchivosTelecredito.Count + 1
Dim registro(0 To lineas) As String
registro(0) = Me.ObtenerCabeceraArchivoTelecredito(telecredito)
Dim archivo = ruta & ".txt"
Using escritor As New StreamWriter(archivo)
For index = 0 To lineas
escritor.WriteLine(registro(index))
Next
escritor.Close()
End Using
Dim lector As FileStream
lector = File.Open(archivo, FileMode.Open)
Dim bytes(lector.Length) As Byte
lector.Read(bytes, 0, lector.Length)
lector.Close()
Return bytes
End Function
If you want to set the file to be read-only then you can use
File.SetAttributes("PathToFile", FileAttributes.ReadOnly).
You could also set the permissions on the directory itself instead of the individual files - see this post: https://serverfault.com/questions/3878/is-there-a-way-to-prevent-a-file-from-being-deleted