3

Currently we have a method that returns a string with a formatted CSV file.

string   = EXPORT.tickets

We need to upload this csv file to a ftp server like so

ftp = Net::FTP.new(server, username, password)
ftp.putbinaryfile(string)

however, the string variable is obviously a string, and not a binary file as the putbinaryfile method expects. I see two ways to do this,

  1. convert the string variable to a file first using File
  2. convert the string directly to a file with something like StringIO

Do these seem like viable options? If so, how would I approach doing this, thanks in advance!

EDIT:

Since the putbinaryfile method is looking for a file path rather than an actual file, it looks like my best best will be to create a File from the string variable. Can anyone give an example of how this can be accomplished?

ruevaughn
  • 1,319
  • 1
  • 17
  • 48
  • This is not 100%, `putbinaryfile` expects the PATH of a binary file (which could be a string). I would probably first create a File (see http://devdocs.io/ruby/file#method-c-new) and then upload it. Don't forget to delete the file after uploading it :) – mhutter Jan 21 '15 at 17:41
  • 1
    You are correct, it is looking for the PATH. What do you think of me using the Ruby Tempfile class? http://devdocs.io/ruby/tempfile – ruevaughn Jan 21 '15 at 17:52
  • Sounds good! One more thing: I THINK you can use `puttextfile` instead of `putbinaryfile`, as CSV is just text, but be sure to test this first... – mhutter Jan 21 '15 at 17:58
  • Alright thanks, I edited my question. Basically i've been having trouble from the start getting the string into an actual file, so if you have any thoughts feel free to post an answer :) – ruevaughn Jan 21 '15 at 18:04

2 Answers2

4

After talking to another developer, he gave me this solution which I found to be a better for my situation, since the file did not exist already. It skips writing the string to a Tempfile and uses StringIO to upload it directly. His solution:

The Net::FTP#putbinaryfile method takes the name of a file on the local filesystem to copy to the remote filesystem. Now, if your data is in a string (and wasn't read from a file on the filesystem) then you may want to use Net::FTP#storbinary instead:

require 'stringio'
require 'net/ftp'

BLOCKSIZE = 512 
data      = StringIO.new("Hello, world!\n")
hostname  = "ftp.domain.tld"
username  = "username"
password  = "password"

remote_filename = "something.txt"

Net::FTP.open(hostname, username, password) do |ftp|
  # ...other ftp commands here...
  ftp.storbinary("STOR #{remote_filename}", data, BLOCKSIZE)
  # ...any other ftp commands...
end

The above avoids writing data that's not on disk to disk, just so you can upload it somewhere. However, if the data is already in a file on disk, you might as well just fix your code to reference its filename instead.

ruevaughn
  • 1,319
  • 1
  • 17
  • 48
3

Something like this should cover most of the bases:

require 'tempfile'

temp_file = Tempfile.new('for_you')
temp_file.write(string)
temp_file.close

ftp.putbinaryfile(temp_file)

temp_file.unlink

Using Tempfile relieves you from a lot of issues regarding unique filename, threadsafeness, etc. Garbage collection will ensure your file gets deleted, even if putbinaryfile raises an exception or similar perils.

The uploaded file will get a name like for_you.23423423.423.423.4, both locally and on the remote server. If you want it to have a specific name on the remote server like 'daily_log_upload', do this instead:

ftp.putbinaryfile(temp_file, 'daily_log_upload')

It will still have a unique name for the local temp file, but you don't care about that.

David Hempy
  • 5,373
  • 2
  • 40
  • 68