How do you delete a file generated via webapi after returning the file as response? - vb.net

I'm creating a file on the fly on a WebAPI call, and sending that file back to the client.
I think I'm misunderstanding flush/close on a FileStream:
Dim path As String = tempFolder & "\" & fileName
Dim result As New HttpResponseMessage(HttpStatusCode.OK)
Dim stream As New FileStream(path, FileMode.Open)
With result
.Content = New StreamContent(stream)
.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment")
.Content.Headers.ContentDisposition.FileName = fileName
.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/octet-stream")
.Content.Headers.ContentLength = stream.Length
End With
'stream.Flush()
'stream.Close()
'Directory.Delete(tempFolder, True)
Return result
You can see where I've commented things out above.
Questions:
Does the stream flush/close itself?
How can I delete the tempFolder after returning the result?
On top of all this, it would be great to know how to generate the file and send it to the user without writing it to the file system first. I'm confident this is possible, but I'm not sure how. I'd love to be able to understand how to do this, and solve my current problem.
Update:
I went ahead with accepted answer, and found it to be quite simple:
Dim ReturnStream As MemoryStream = New MemoryStream()
Dim WriteStream As StreamWriter = New StreamWriter(ReturnStream)
With WriteStream
.WriteLine("...")
End With
WriteStream.Flush()
WriteStream.Close()
Dim byteArray As Byte() = ReturnStream.ToArray()
ReturnStream.Flush()
ReturnStream.Close()
Then I was able to stream the content as bytearraycontent:
With result
.Content = New ByteArrayContent(byteArray)
...
End With

On top of all this, it would be great to know how to generate the file and send it to the user without writing it to the file system first. I'm confident this is possible, but I'm not sure how. I'd love to be able to understand how to do this, and solve my current problem.
To do the same thing without writing a file to disk, you might look into the MemoryStream class. As you'd guess, it streams data from memory like the FileStream does from a file. The two main steps would be:
Take your object in memory and instead of writing it to a file, you'd serialize it into a MemoryStream using a BinaryFormatter or other method (see that topic on another StackOverflow Q here: How to convert an object to a byte array in C#).
Pass the MemoryStream to the StreamContent method, exactly the same way you're passing the FileStream now.

Related

VB.NET Save XMLdocument as UTF-8 with BOM

I have written a test application to modify a few hundred XMLs, basically I modify a few nodes and then save the XML again.
The input XML files are UTF-8-BOM but the output is UTF-8 (as viewed in Notepad++).
The code runs on a console application in VB.NET 4.7.2 and this is my basic code:
Dim myXML As XmlDocument = New XmlDocument
Dim nodelist As XmlNodeList
Dim node As XmlNode
myXML.Load(file)
nodelist = myXML.SelectNodes("//root/row")
For Each node In nodelist
'All my code goes here
Next
myXML.Save(file)
I tried things like:
myXML.CreateXmlDeclaration("1.0", "UTF-16", "")
But that didn't work. I have been searching and it seems everyone has the exact oposite issue and checking in the MSDOC I can't see any reference to specify BOM or not:
https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmldeclaration.encoding?view=netframework-4.7
The issue is that when the files are imported to the DB without BOM some characters show corruption so I really need the same encoding.
After so much reading I could try to rewrite my app using StreamWriter instead XMLDocument but if there is a workaround about that I would pretty much prefer it :). Thanks!
As suggested by #JosefZ I used this strategy:
Using writer = New XmlTextWriter(file, New UTF8Encoding(True))
Dim xmlsettings As XmlWriterSettings = New XmlWriterSettings
xmlsettings.OmitXmlDeclaration = False
xmlsettings.ConformanceLevel = ConformanceLevel.Fragment
xmlsettings = writer.Settings
myXML.Save(writer)
End Using

VB.Net FTP read of dynamically generated file where FTPGetFileSize is not supported

I've been struggling with trying to read the configuration files on a device via FTP. The files are generated dynamically when the FTP request is sent, and the device does not support the FTPGetFileSize command.
I've lost count of the number of examples from the Web that I've tried in an attempt to solve this. Part of the problem is likely due to my inexperience with file streams. So, I'm looking for a kind soul that might be able to provide some insight.
In the code below, I'm trying two different methods. The first is a straight read of the entire file, the second attempts to control the read based whether there is anything coming in over the stream.
Both methods work on other devices that support the FTPGetFileSize command. But neither method works for this device.
Project form
Imports are:
Imports System.Net
Imports System.IO
Imports System.Text
The cmdGet_Click event code looks like this:
Dim sLocal_Path As String = "C:\Temp\FTP_Test\" + txtIPAddress.Text + "\"
System.IO.Directory.CreateDirectory(sLocal_Path)
' Complete the local path
sLocal_Path += txtFileName.Text
If System.IO.File.Exists(sLocal_Path) Then
System.IO.File.Delete(sLocal_Path) ' Make sure we don't have a collision.
End If
' Set up the Remote Path.
Dim sRemote_Path As String = "ftp://" + txtIPAddress.Text + "/gen:" + txtFileName.Text
Dim request As FtpWebRequest = CType(WebRequest.Create(sRemote_Path), FtpWebRequest)
request.Method = WebRequestMethods.Ftp.DownloadFile
request.Credentials = New NetworkCredential(FTP_User, FTP_Password)
request.UsePassive = True
request.Timeout = 15000
request.UseBinary = False
Dim response As FtpWebResponse = CType(request.GetResponse(), FtpWebResponse)
Dim responseStream As Stream = response.GetResponseStream()
Dim reader As StreamReader = New StreamReader(responseStream)
Dim writeFile As StreamWriter = New StreamWriter(sLocal_Path)
If RadioReadToEnd.Checked Then
' Write the FTP stream to the target file
writeFile.Write(reader.ReadToEnd()) ' works with static files.
Else
' Write the FTP stream as long as we have data coming in.
Dim ByteCount As Integer = 0
Threading.Thread.Sleep(500)
While Not (reader.Peek() = -1)
ByteCount += 1
If ByteCount > 4095 Then ' Go up for air after every block.
ByteCount = 0 ' Reset the byte counter for the next block.
Application.DoEvents()
End If
writeFile.Write(Chr(reader.Read))
End While
End If
reader.Close()
reader.Dispose()
writeFile.Close()
writeFile.Dispose()
responseStream.Close()
responseStream.Dispose()
Note that I put a delay in just before the While Loop in the hopes that maybe something would show up in the stream to keep the loop from terminating. Apparently I need some way to prime the pump.
Edit: The DOS FTP Get works fine. My assumption is that it has some way to get around the FTPGetFileSize not being supported. Is there a way to get the WebClient FTP Methods to do the same thing with a dynamically generated file?
I need a nudge (okay, a kick) in the right direction. Any suggestions are greatly appreciated!
It is now working! The problem turned out to be a missing '/' at the root of the path.
This particular device has a drive reference in it's path that other devices my routine talks to did not have. So, between that and the error being thrown regarding the file size, I was put off of the scent.
Thanks to Martin Prikryl's comment about showing a log, I went off to create a Wireshark recording, and that's when I noticed the missing slash when I compared it to the DOS FTP Get recording. So, while I'm a little red-faced, I hope my experience helps someone else who runs into a similar problem. But mostly, I'm very relieved that I did not have to go the route of writing an FTP Client that dealt with raw commands such as POST and RETR! The WebClient tools are now working very well for this application.

how to read a CSV file as resource using TextFieldParser

I have a CSV file in my project resources which I want to read using FileIO.TextFieldParser
I tried Dim parser = new TextFieldParser(My.Resources.ArticlesCSV), but since TextFieldParser expects either a path (as string) or a stream, this is not working.
I guess one possibility is to convert the resource to a stream, but I cannot find how to do that...
What is the best way to get this working?
You can create a new instance of IO.StringReader which is of type TextReader that TextFieldParser will accept. Just pass your CSV file (Thanks to AndrewMorton)
Using strReader As New IO.StringReader(My.Resources.ArticlesCSV)
Using textparser As New TextFieldParser(strReader)
textparser.Delimiters = {","}
While Not textparser.EndOfData
Dim curRow = textparser.ReadFields()
' Do stuff
End While
End Using
End Using

Writing to FileStream works, MemoryStream copied to FileStream doesn't

I have some code that used a FileStream, StreamWriter and XmlDocument to produce Excel-compatible output files. Very useful!
However I now have a need to make copies of the file, and I'd like to do that in-memory. So I took my original FileStream code and changed the FileStream to a MemoryStream, and then wrapped that in this function:
'----------------------------------------------------------------------------------
Friend Sub Save(Optional ByVal SaveCalculatedResults As Boolean = True)
Dim MStream As MemoryStream
Dim FStream As FileStream
Dim Bytes As Byte()
'make the stream containing the XML
MStream = ToXLSL(SaveCalculatedResults)
If MStream.Length = 0 Then Return
'then read that data into a byte buffer
ReDim Bytes(CInt(MStream.Length))
MStream.Read(Bytes, 0, CInt(MStream.Length))
'and then write it to "us"
FStream = New FileStream("C:\OUTFILE.XLSX", FileMode.Create)
FStream.Write(Bytes, 0, CInt(MStream.Length))
FStream.Flush()
End Sub
This creates a file in the correct location, it has the exact same length as it did before, but opening it in Excel causes an error about the file format being invalid.
Can anyone see any obvious problems in that code? Perhaps I am writing the bytes backwards? Is this possibly a text encoding problem? 32/64 problem?
p.s. I tried using CopyTo, but that doesn't seem to work in VB?
It requires guessing what ToXLSL() does but the behavior gives a strong hint: the MemoryStream's Position is located at the end of the stream. So the Read() call doesn't actually read anything. Verify by checking its return value.
Just get rid of Bytes() entirely, it is very wasteful to duplicate the data like this. You don't need it, the MemoryStream already gives you access to the data:
Using FStream = New FileStream("C:\OUTFILE.XLSX", FileMode.Create)
FStream.Write(MStream.GetBuffer(), 0, CInt(MStream.Length))
End Using
Do note that the Using statement is not optional. And that you cannot write to C:\

Track webpage downloading progress

I'm using this piece of code for VB.NET to download the text from a website:
Dim Str As System.IO.Stream
Dim srRead As System.IO.StreamReader
Dim req As System.Net.WebRequest = System.Net.WebRequest.Create("http://www.example.com/file.txt")
Dim resp As System.Net.WebResponse = req.GetResponse
Str = resp.GetResponseStream
srRead = New System.IO.StreamReader(Str)
It is just a text file, and is rather small, so it downloads really quickly. But I do believe that in the future the file will become considerably large. Is there a way to track the downloading progress from the above method?
You can find the total length of your find in the ContentLength property of your WebResponse object. Once you have that, it's pretty easy to report progress based on the data you read from the GetResponseStream.