1

I want to store a binary file using Ruby's Net::FTP class. The content for the file is put into a pipe by a previous process. I need to take the bytes from the pipe (IO class) and store it (on the fly/no temporary file) to the ftp server.

If I do it this way

ftp = Net::FTP.new(@@host, @@user, @@password)
ftp.debug_mode = true 
ftp.passive = true
ftp.binary = true
ftp.storbinary("STOR #{name}", pipe, Net::FTP::DEFAULT_BLOCKSIZE)
ftp.close

the size of the stored file is about 500kb smaller than it should be (correct size is about 6,8 MB). The file contains gpg encrypted data. If try to decrypt, I get an error.

Storing directly from the pipe to a local file results in a file with right size and working decryption.

I'm relatively new to ruby, can someone give me a hint? Some idea for debugging? Can I provide additional informations?

Thanks for your help


Debug output from Net::FTP:

put: PASV
get: 227 Entering Passive Mode (80,237,136,162,233,60).
put: STOR test-ftp.tar.gz.gpg
get: 150 Opening BINARY mode data connection for test-ftp.tar.gz.gpg
get: 226 Transfer complete

Ruby version: ruby 2.1.5p273

OS: debian linux

Environment: ruby script is executed directly from bash

Some more code:

p_out, p_in = IO.pipe
@@thread = Thread.new {
  cmd = "gpg --no-tty --cipher-algo AES256 --compress-level 0 --passphrase-file #{@@cmd.results[:gpg_passphrase_file]} --symmetric" 
  # Execute gpg
  Open3.popen3 ( cmd )  { |stdin, stdout, stderr, wait_thr|   

    Thread.new {
      cnt = IO::copy_stream pipe, stdin
      @@output.debug "GPG_Encryption::execute copied #{(Float(cnt)/1024/1024).round(2)} MiB bytes to gpg"
      pipe.close
      stdin.close
    }

    Thread.new {
      cnt = IO::copy_stream stdout, p_in
      @@output.debug "GPG_Encryption::execute copied #{(Float(cnt)/1024/1024).round(2)} MiB bytes from gpg"
    }

    # wait for gpg finished
    wait_thr.join

    # Close pipe (sends eof)
    p_in.close

    # check result
    if 0 == wait_thr.value 
      @@output.info "gpg finished..."                    
    else
      @@output.error "gpg returned an error"
      @@output.raw stderr.readlines.join
      exit 1
    end
  }
}

ftp = Net::FTP.new(@@host, @@user, @@password)
ftp.debug_mode = true 
ftp.passive = true
ftp.binary = true
ftp.storbinary("STOR #{name}", pipe, Net::FTP::DEFAULT_BLOCKSIZE)
ftp.close

1 Answers1

0

You might be closing the FTP connection before the file has been completely sent. Try removing the explicit ftp.close. You probably don't need it anyway as the connection will be closed automatically when ftp is garbage collected.

davogones
  • 7,321
  • 31
  • 36
  • Thanks davogones, i just tried it, but it makes no difference. The stored file is still to small. But what if the whole script ends before the transfer is done? Is there a way to wait for everything sent, like join for threads? – PiperAtTheGatesOfDawn Feb 08 '15 at 09:41