Zipfile in vb.net 4.5 "The directory name is invalid." error - can't identify the cause? - vb.net

I've got a small utility that can encrypt/decrypt using .net 4.5 TripleDES routines. I added compression and found the simpler compression stuff doesn't work for me.
The files referenced in these fields definitely exist, aren't huge at all and are even named in the same case.
Yet I keep getting "The directory name is invalid.". If I take out the test for existence of the archive, I get a different error - as it's made the zip and it's 0 bytes. I've searched for far longer on this than it took to work out how to use the encryption part of my utility yesterday - and that's with learning how streams work!
Can anyone shed light on this issue at all please?
Private Encoded As String = "C:\TestFile\encoded.txt"
Private Decoded As String = "C:\TestFile\decoded.txt"
Private CompressEncoded As String = "C:\TestFile\encoded.zip"
Private Sub Main()
Compress(Encoded, CompressEncoded)
End sub
Sub Compress(filename As String, zippedFile As String)
'Added to handle file already exists error
If IO.File.Exists(zippedFile) Then IO.File.Delete(zippedFile)
If IO.File.Exists(filename) Then IO.Compression.ZipFile.CreateFromDirectory(filename, zippedFile, CompressionLevel.Fastest, True)
End Sub
In addition to this code I also tried adding a quick streamreader test to prove to myself that filename does exist.
' Dim sr As New StreamReader(filename)
MessageBox.Show(sr.ReadToEnd)
It does and displays the line of text within it.
I'd be grateful for any help - this rather silly bug has chewed up lots of time this afternoon that I was hoping to use on something more useful :).
Thanks folks!
Thanks for the answer!
Sub Compress(filename As String, zippedFile As String)
If IO.File.Exists(zippedFile) Then IO.File.Delete(zippedFile)
If IO.File.Exists(filename) Then
Using archive As ZipArchive = Open(zippedFile, ZipArchiveMode.Create)
archive.CreateEntryFromFile(filename, Path.GetFileName(filename), CompressionLevel.Fastest)
End Using
End If
End Sub
Sub Decompress(ZippedFile As String, UnzippedFile As String)
If IO.File.Exists(UnzippedFile) Then IO.File.Delete(UnzippedFile)
If IO.File.Exists(ZippedFile) Then
Using archive As ZipArchive = Open(ZippedFile, ZipArchiveMode.Read)
archive.ExtractToDirectory(Path.GetDirectoryName(UnzippedFile))
End Using
End If
End Sub

Probably it is a simple error.
The first parameter to pass to CreateFromDirectory should be a directory name, but you pass a file name.
Try with
If IO.File.Exists(filename) Then
IO.Compression.ZipFile.CreateFromDirectory(Path.GetDirectoryName(filename), _
zippedFile, CompressionLevel.Fastest, True)
End If

If you want to compress a single file, use ZipArchive class like below:
Sub Compress(filename As String, zippedFile As String)
If IO.File.Exists(zippedFile) Then IO.File.Delete(zippedFile)
If IO.File.Exists(filename) Then
Using archive As ZipArchive = ZipFile.Open(zippedFile, ZipArchiveMode.Create)
archive.CreateEntryFromFile(filename, Path.GetFileName(filename), CompressionLevel.Fastest)
End Using
End If
End Sub
ADDITIONAL INFO
Need to add reference to System.IO.Compression.dll for the ZipArchive and System.IO.Compression.FileSystem.dll for the ZipFile class. These DLLs are not referenced by default when a project is created in VS.

Related

Simple code that reads CSV values causes an error in System.IO.Directory

I can't seem to figure out why I'm getting a compilation error with this code that tries to find the most recently updated file (all CSV files) in a directory, to then pull the last line of the CSV and update a device.
The exception I get is:
Line 3 Character 10 expected end of statement.
Don't worry about the hs.SetDevice, I know that part is correct.
Imports System.IO
Sub Main()
Dim path = System.IO.DirectoryInfo.GetFiles("C:\Users\Ian\Documents\Wifi Sensor Software").OrderByDescending(Function(f) f.LastWriteTime).First()
Dim line = System.IO.File.ReadLines(path).Last()
Dim fields() = line.Split(",".ToCharArray())
Dim fileTemp = fields(2)
hs.SetDeviceValueByRef(124, fileTemp, True)
End Sub
EDIT:
Changed Directory to DirectoryInfo
The original problem was that Directory.GetFiles() returns an array of strings, a string doesn't have a LastWriteTime Property.
This property belongs to the FileInfo base class, FileSystemInfo, the object type returned by DirectoryInfo.GetFiles().
Then, a FileInfo object cannot be passed to File.ReadLines(), this method expects a string, so you need to pass [FileInfo].FullName.
Hard-coding a Path in that manner is not a good thing. Use Environment.GetFolderPath() to get the Path of special folders, as the MyDocuments folder, and Path.Combine() to build a valid path.
Better use the TextFieldParser class to parse a CSV file. It's very simple to use and safe enough.
The worst problem is Option Strict set to Off.
Turn it On in the Project's Properties (Project->Properties->Compile), or in the general options of Visual Studio (Tools->Options->Projects and Solutions->VB Defaults), so it's already set for new Projects.
You can also add it on top of a file, as shown here.
With Option Strict On, you are immediately informed when a mishap of this kind is found in your code, so you can fix it immediately.
With Option Strict Off, some issues that come up at run-time can be very hard to identify and fix. Setting it On to try and fix the problem later is almost useless, since all the mishaps will come up all at once and you'll have a gazillion of error notifications that will hide the issue at hand.
Option Strict On
Imports System.IO
Imports Microsoft.VisualBasic.FileIO
Dim filesPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments), "Wifi Sensor Software")
Dim mostRecentFile = New DirectoryInfo(filesPath).
GetFiles("*.csv").OrderByDescending(Function(f) f.LastWriteTime).First()
Using tfp As New TextFieldParser(mostRecentFile.FullName)
tfp.TextFieldType = FieldType.Delimited
tfp.SetDelimiters({","})
Dim fileTemp As String = String.Empty
Try
While Not tfp.EndOfData
fileTemp = tfp.ReadFields()(2)
End While
Catch fnfEx As FileNotFoundException
MessageBox.Show($"File not found: {fnfEx.Message}")
Catch exIDX As IndexOutOfRangeException
MessageBox.Show($"Invalid Data format: {exIDX.Message}")
Catch exIO As MalformedLineException
MessageBox.Show($"Invalid Data format at line {exIO.Message}")
End Try
If Not String.IsNullOrEmpty(fileTemp) Then
hs.SetDeviceValueByRef(124, fileTemp, True)
End If
End Using

Get file size before download it in vb

I have been working on a web browser in visual basic..Now,what I want to do is to the get file size before download it and when I click download I want to to get the number of the alrady downloaded Mbs(watch the picture)
Thank's for help!
I've done some research, and this would probably be the most simple and "cleanest" way of getting a download's size (in bytes):
Public Function GetDownloadSize(ByVal URL As String) As Long
Dim r As Net.WebRequest = Net.WebRequest.Create(URL)
r.Method = Net.WebRequestMethods.Http.Head
Using rsp = r.GetResponse()
Return rsp.ContentLength
End Using
End Function
Credit to Reed Kimble, who told me to dispose the WebResponse in my initial MSDN question.
The above code will read the response headers of the file, rather than reading the body of it. This means that the file does not require to get downloaded, just to check it's size.
This is the reason to why some codes requires the file to actually get downloaded at first; they're reading the file's body rather than it's headers.
Hope this helps!
Use the WebClient ResponseHeaders:
Public Shared Function GetFileSize(url As String) As Long
Using obj As New WebClient()
Using s As Stream = obj.OpenRead(url)
Return Long.Parse(obj.ResponseHeaders("Content-Length").ToString())
End Using
End Using
End Function
Request file size before download it
The WebClient's DownloadProgressChanged event's args contains the property TotalBytesToRecieve. That tells you how many bytes the file you're downloading is.
Not the prettiest way, but if you want to get the size of the file before downloading you can start downloading the file then immediately cancel it:
Dim DownloadSize As Long
Private Sub CheckDownloadSize(ByVal URL As String)
WebClient.DownloadFile(URL, IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "tempdownload.tmp"))
End Sub
Private WithEvents WebClient As New WebClient
Private Sub WebClient_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles WebClient.DownloadProgressChanged
DownloadSize = e.TotalBytesToReceive
WebClient.CancelAsync()
End Sub
Otherwise, just remove the .CancelAsync() line.

How to get the file name of a file in VB?

I make a search program for searching a list of files in a computer and then copy the file into a store folder. The file name could be "*11*2.txt" As long as the program find this pattern, it should copy to the store folder. The problem is that I don't know the exactly name of the file before the search and I don't want to rename the file, I don't know how to save the file. Please help
I use the following to find the file, which does its work
Public Sub DirSearch(ByVal sDir As String, ByVal FileName As String)
Dim To_Path As String
To_Path = Form1.TextBox5.Text
For Each foundFile As String In My.Computer.FileSystem.GetFiles(sDir, FileIO.SearchOption.SearchAllSubDirectories, FileName)
Copy2Local(foundFile, To_Path)
Next
End Sub
Here is the current version of the Copy2Local (Note: it is not working right)
Public Sub Copy2Local(ByVal Copy_From_Path As String, ByVal Copy_To_Path As String)
' Specify the directories you want to manipulate.
Try
Dim fs As FileStream = File.Create(Copy_From_Path)
fs.Close()
' Copy the file.
File.Copy(Copy_From_Path, Copy_To_Path)
Catch
End Try
End Sub
First, you should check if ToPath is a valid directory since it's coming from a TextBox:
Dim isValidDir = Directory.Exists(ToPath)
Second, you can use Path.Combine to create a path from separate (sub)directories or file-names:
Dim copyToDir = Path.GetDirectoryName(Copy_To_Path)
Dim file = Path.GetFileName(Copy_From_Path)
Dim newPath = Path.Combine(copyToDir, file)
http://msdn.microsoft.com/en-us/library/system.io.path.aspx
(disclaimer: typed from a mobile)
To answer your question: You can get the file name with Path.GetFileName. Example:
Dim fileName As String = Path.GetFileName(foundFile)
However, there's a bunch of other things wrong with your code:
Here,
Dim fs As FileStream = File.Create(Copy_From_Path)
fs.Close()
you are overwriting your source file. This does not seem like a good idea. ;-)
And here,
Try
...
Catch
' Do Nothing
End Try
You are throwing away exceptions that would help you find and diagnose problems. Don't do that. It makes debugging a nightmare.
In vb.net, I'm using the following code to find the filename
Textbox1.Text = New FileInfo(OpenFileDialog.FileName).Name
this code work fine with open file dialog box

Searching specific folder / extension using vb.net

I How can I search a specific file using vb.net and store the path in a variable?
For example if I need to know where I have *.abc files in my entire computer, how can this be done?
Thanks
Furqan
Dim di As New DirectoryInfo("c:\")
Dim files() As FileInfo = di.GetFiles("*.abc", SearchOption.AllDirectories)
There's the EnumerateFiles method that has been introduced in .NET 4.0. If not you could use the GetFiles method but be warned that this method returns an array of strings which represent the matched filenames and it could block for a long time.
Well this is awkward, but this seems to work for me before, maybe you can try it
If System.IO.File.Exists(txtName.Text) Then
MsgBox("Match not found")
Else
MsgBox("Match found")
End If
Update
This one works
Directory.SetCurrentDirectory(My.Computer.FileSystem.SpecialDirectories.MyDocuments & "\WinVault")
If Not System.IO.File.Exists(txtName.Text & ".wv") Then
btnSave.Enabled = True
Else
btnSave.Enabled = False
'Balloon tip
bTipControl = txtName
bTipCaption = "Vault Name"
bTipText = VAULT_NAME_EXIST
bTip_Show()
End If
And of course, make sure you do import System.IO or add reference if not available.

VB.NET Read Certain text in a text file

I want my program to read certain text in a text file. For example if I have a text file that contains the following info..
acc=blah
pass=hello
I want my vb.net application to get that the account variable is equal to blah, and the password variable is equal to hello.
Can anyone tell me how to do this?
Thanks
Here is a quick little bit of code that, after you click a button, will:
take an input file (in this case I created one called "test.ini")
read in the values as separate lines
do a search, using regular expressions, to see if it contains any "ACC=" or "PASS=" parameters
then write them to the console
here is the code:
Imports System.IO
Imports System.Text.RegularExpressions
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim strFile As String = "Test.INI"
Dim sr As New StreamReader(strFile)
Dim InputString As String
While sr.Peek <> -1
InputString = sr.ReadLine()
checkIfContains(InputString)
InputString = String.Empty
End While
sr.Close()
End Sub
Private Sub checkIfContains(ByVal inputString As String)
Dim outputFile As String = "testOutput.txt"
Dim m As Match
Dim m2 As Match
Dim itemPattern As String = "acc=(\S+)"
Dim itemPattern2 As String = "pass=(\S+)"
m = Regex.Match(inputString, itemPattern, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled)
m2 = Regex.Match(inputString, itemPattern2, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled)
Do While m.Success
Console.WriteLine("Found account {0}", _
m.Groups(1), m.Groups(1).Index)
m = m.NextMatch()
Loop
Do While m2.Success
Console.WriteLine("Found password {0}", _
m2.Groups(1), m2.Groups(1).Index)
m2 = m2.NextMatch()
Loop
End Sub
End Class
Have a look at this article
Reading and writing text files with VB.NET
Wile reading the file line by line, you can use String.Split Method with the splitter being "=", to split the string into param name, and param value.
Looks like you've got an INI file of some kind... The best way to read these is using the *PrivateProfile* functions of the windows API, which means you can actually have a proper full INI file quite easily for anything you need. There is a wrapper class here you may like to use.
Microsoft recommends that you use the registry to store this sort of information though, and discourages use of INI files.
If you wish to just use a file manually with the syntax you have, it is a simple case of splitting the string on '=' and put the results into a Dictionary. Remember to handle cases where the data was not found in the file and you need a default/error. In modern times though, XML is becoming a lot more popular for data text files, and there are lots of libraries to deal with loading from these.
My suggestion: you use XML. The .NET framework has a lot of good XML tools, if you're willing to make the transition to put all the text files into XML, it'll make life a lot easier.
Not what you're looking for, probably, but it's a cleaner solution than anything you could do with plain text (outside of developing your own parser or using a lower level API).
You can't really selectively read a certain bit of information in the file exclusively. You'll have to scan each line of the file and do a search for the string "pass=" at the beginning of the line. I don't know VB but look up these topics:
File readers (espically ones that can read one line at a time)
String tokenizers/splitting (as Astander mentioned)
File reading examples
Have you thought about getting the framework to handle it instead?
If you add an entry into the settings tab of the project properties with name acc, type string, scope user (or application, depending on requirements) and value pass, you can use the System.Configuration.ApplicationSettingsBase functionality to deal with it.
Private _settings As My.MySettings
Private _acc as String
Private _pass as String
Public ReadOnly Property Settings() As System.Configuration.ApplicationSettingsBase
Get
If _settings Is Nothing Then
_settings = New My.MySettings
End If
Return _settings
End Get
End Property
Private Sub SetSettings()
Settings.SettingsKey = Me.Name
Dim theSettings As My.MySettings
theSettings = DirectCast(Settings, My.MySettings)
theSettings.acc=_acc
theSettings.pass=_pass
Settings.Save()
End Sub
Private Sub GetSettings()
Settings.SettingsKey = Me.Name
Dim theSettings As My.MySettings
theSettings = DirectCast(Settings, My.MySettings)
_acc=theSettings.acc
_pass=theSettings.pass
End Sub
Call GetSettings in whatever load event you need, and SetSettings in closing events
This will create an entry in the application.exe.config file, either in your local settings \apps\2.0\etc etc directory, or your roaming one, or if it's a clickonce deployment, in the clickonce data directory. This will look like the following:-
<userSettings>
<MyTestApp.My.MySettings>
<setting name="acc" serializeAs="String">
<value>blah</value>
</setting>
<setting name="pass" serializeAs="String">
<value>hello</value>
</setting>
</MyTestApp.My.MySettings>
</userSettings>
Writing your own parser is not that hard. I managed to make one for a game (Using C#, but VB appears to have Regex class too. Using that, the acc variable in your file would be everything up to the = sign, and then blah would be everything past the = to the newline character (\n) Then go to the next line and repeat.
I have written this for you, check it and enjoy with the results, have a great day!
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim acc As New List(Of String)
Dim pass As New List(Of String)
Dim lines() As String = System.IO.File.ReadAllLines(".\credentials.txt")
For Each lineItem As String In lines
Dim vals() As String = lineItem.Split(Convert.ToChar("="))
If vals.Length > 0 Then
Dim lineId As String = vals(0)
If lineId = "acc" Then
acc.Add(vals(1))
ElseIf lineId = "pass" Then
pass.Add(vals(1))
End If
End If
Next
TextBox_acc.Text = String.Join(Environment.NewLine, acc)
TextBox_pass.Text = String.Join(Environment.NewLine, pass)
End Sub
End Class