zip.SaveSelfExtractor from server to local drive

Nov 11, 2010 at 5:51 PM

Hi,

First of all thank you for your fine library !

i have been trying to do the following:

using a vb.net 4.0 webapp

the DotNetZipLib-DevKit-v1.9 DLL

- on the server side, loop through a list of file names on the server, add them to a zip file and output a selfextractor .exe file for a user to download the file to his local drive and extract it.

i have tried collecting all kinds of examples of code to achieve this, using this method, i can download archive.exe but when i try to open it on vista (as administrator) i get this error:

16 bit MS-DOS Subsystem

"The NTVDM CPU has encountered an illegal instruction.

CS:127c IP:09ee OP:63 32 6c 30 5a Choose 'close' to terminate the application"

if i click ignore then the command window just stays open and nothing happens.

can you try and help ? thanks

 

here is the code i use:

    Protected Sub Download1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Download1.Click
        Try

            Dim emptyCheckboxList As Boolean = False
            Dim ItemsNum As Int32 = chklst.Items.Count()

            Response.Clear()
            Response.BufferOutput = False

            Response.AddHeader("Content-disposition", "attachment; filename=" + "archive.exe")
            Response.AddHeader("Content-Description", "File Transfer")
            Response.AddHeader("Content-Transfer-Encoding", "binary")
            Response.ContentType = "application/exe"

            Using zip As New ZipFile()

                For Each lstItem In chklst.Items

                    If lstItem.Selected = True Then
                        emptyCheckboxList = True
                        debug1.Text = debug1.Text & "downloaded: " & lstItem.ToString & "<br />"
                        zip.AddFile(lstItem.ToString, "")
                    End If
                Next
                zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication)
            End Using


            If emptyCheckboxList = False Then
                ErrorLbl.Text = "No File was chosen for download, please select files "
            Else
                ErrorLbl.Text = ""
            End If

        Catch ex As Exception
            ErrorLbl.Text = "Download_Click: " & ex.Message
            Return
        End Try

    End Sub

Coordinator
Nov 11, 2010 at 6:55 PM

I suspect the reason you get the instruction error is that the EXE image on the web browser machine is either empty or else contains only junk.

What you have looks pretty good, except...

  1. You're saving the self-extractor to a filename called "archive.exe".  This is going to be a file that is local to the web server.  You will want to explicitly specify a temporary directory path here, eg c:\temp\iisdirectory or something.  I'm surprised that the code, as written, is working.  Typically ASPNET does not have write permissions in the "current working directory" where it runs, which is often c:\windows\system32.   So you will want to explicitly specify the directory to hold the temporary zip file on the server.  You want this in an explicit, well-known location, to ease the job of removal of any stray temporary files, should that be necessary.  You could also use System.IO.Path.GetTempPath  or Environment.GetEnvironmentVariable("temp") for this location.  I won't advise you on the pros and cons of each of those options.
  2. Grant WRITE permissions to the ASPNET user to that temporary directory.  You could also just use the IIS_USRS group.  (I think that is the correct spelling). Grant write permissions on your desired temp staging directory on the server to that user or group.
  3. In the current code, you don't actually write the content of archive.exe to the Response.OutputStream.  The way you have it written, you prepare the Response.OutputStream to get content, then you save a zipfile to the filesystem on the server, and nothing gets written to the web browser.  To write the content to the web browser,  AFTER calling zip.Save("archive.exe"...),  and outside the scope of the containing Using clause, you will need to open a filestream on "archive.exe" or whatever you named your temp file, then read the bytes from that temp file, and write them to Response.OutputStream.
  4. Finally, call Response.Close().
  5. After all the bytes are streamed to the browser, and you've called Response.Close(), delete the temporary file with System.IO.File.Delete("whatever").

 

Good luck.

 

 

 

Nov 12, 2010 at 4:01 PM

hi Cheeso, youre  a life saver ! thanks

if anyone might find this useful , here is the code, it might be a bit messy but it works for me:

 

   Protected Sub Download1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Download1.Click
        Try

            Dim emptyCheckboxList As Boolean = False
            Dim ItemsNum As Int32 = chklst.Items.Count()
            Dim MyFileStream As FileStream
            Dim FileSize As Long
            Dim fp As StreamWriter

            Response.Clear()

            Using zip As New ZipFile()

                For Each lstItem In chklst.Items

                    If lstItem.Selected = True Then
                        emptyCheckboxList = True
                        zip.AddFile(Server.MapPath(".\files\") & lstItem.ToString, "")
                    End If
                Next
 		' save self extracting file to server
                zip.SaveSelfExtractor(Server.MapPath(".\files\temp\") & "archive.exe", SelfExtractorFlavor.ConsoleApplication)
            End Using

          
            MyFileStream = New FileStream(Server.MapPath(".\files\temp\") & "archive.exe", FileMode.Open)
            FileSize = MyFileStream.Length

            Dim Buffer(CInt(FileSize)) As Byte
            MyFileStream.Read(Buffer, 0, CInt(FileSize))
            MyFileStream.Close()

            Response.ContentType = "application/exe"
             Response.AddHeader("Content-Disposition", "attachment; filename=archive.exe")
            Response.OutputStream.Write(Buffer, 0, FileSize)
            Response.Flush()
            Response.Close()

            If emptyCheckboxList = False Then
                ErrorLbl.Text = "No File was chosen for download, please select files "
            Else
                ErrorLbl.Text = ""
            End If

        Catch ex As Exception
            ErrorLbl.Text = "Download_Click: " & ex.Message
           
            Return
        End Try
         
    End Sub

Coordinator
Nov 12, 2010 at 5:24 PM

Looks really good.

Couple more things.

  1. FileStream is Disposable.  You should wrap it in a using clause, just as you do with ZipFile. If you don't, then you run the risk of keeping files open longer than necessary on your server, and also not being able to delete the file when your code is finished reading it.
  2. When you read the exe file, you should do so in chunks that are a well-known and reasonable size. Currently you check the size of the file, then allocate an array that is equal to the size of the file, in size, then read in the entire file, then write out that entire array to Response.OutputStream.  This works, but it allocates an array that is of unknown size and potentially very large.  Imagine an exe that is 1gb in size.  You would have to allocate an array of that size.  This will likely fail.   Instead, read the file and write the output in chunks, like this:
Response.ContentType = "application/exe"
Response.AddHeader("Content-Disposition", "attachment; filename=archive.exe")
Using fs as New Filestream("mytempfile.exe")
    Dim Buffer(2048) as Byte
    Dim n as Int32
    Do
        n = fs.Read(Buffer, 0, 2048)
        Response.OutputStream.Write(Buffer, 0, n)
    Loop While n > 0
End Using
Response.Flush()
Response.Close()

Good luck!

 

Nov 16, 2010 at 11:21 AM

Hi Cheeso, thank you for that tip.

i was trying to implement your suggestion and ran into an error "Overload resolution failed because no accessible 'New' accepts this number of arguments Error" coming from this command

Using fs as New Filestream("mytempfile.exe")

so i looked it up and saw you need 3 parameters for Filestream.

i tried using the following:

Using fs as New Filestream(Server.MapPath(".\files\temp\") & exeFileName, FileMode.OpenOrCreate, FileAccess.Read)

 

and it looks like its working now.

Thanks again.

Buzi

 

 


Coordinator
Nov 16, 2010 at 2:06 PM
Edited Nov 16, 2010 at 2:09 PM

Ah, yes, you're correct. Thanks for catching that. I was thinking of File.OpenRead().

If you want to clean up that code a little, you can get a FileStream also from calling the static File.OpenRead() method with a single argument, the filename. Some people prefer the more general approach using New FileStream.

 

Nov 16, 2010 at 4:28 PM

By the way. im not sure if i should open a new thread on this. but im facing an issue with the self extracting file:

on some pc's when trying to open this file i get this error message box:

 

".NET Framework Initialization Error

to run this application you first must install one of the following versions of the .NET Framework v4.0..."

my application is targeting the .NET 4.0 framework.

is there some workaround or a way to bundle the .Net framework with the installed if its not installed on the computer ?

have you seen this issue before ?

Thanks

Buzi

 

Coordinator
Nov 16, 2010 at 7:22 PM

yes, please open a new thread.