# Net::FTP puttextfile question

**URL:** <https://www.ruby-forum.com/t/net-ftp-puttextfile-question/91051>\
**Category:** Ruby\
**Created:** [April 4, 2007, 3:27am UTC](https://www.ruby-forum.com/t/net-ftp-puttextfile-question/91051 "2007-04-04T03:27:32Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [April 4, 2007, 3:27am UTC](https://www.ruby-forum.com/t/net-ftp-puttextfile-question/91051/1 "2007-04-04T03:27:32Z")

</div>

I’m trying to use the Net::FTP class to send a text file to my FTP  
server, but I don’t have an actual text file to send. Instead I want  
to send it a multi-line string as my “file”. (I’m using Builder to  
generate an XML file and I don’t want to have to physically save it to  
a file before sending it.)

The Net::FTP class has a method called puttextfile that seems to be  
defined like this:

def puttextfile(localfile, remotefile = File.basename(localfile),  
&block)

but I don’t have a local filename to provide. Is there anything I can  
use that would substitute for one?

I tried to use a StringIO object, hoping to use that as my  
“localfile”:

s = StringIO.new(xml\_data)

but when I try to send it like this:

Net::FTP.open(“[domain.com](http://domain.com)”, “username”, “secret”) do |ftp|  
ftp.puttextfile(s, ‘\data\file.xml"’)  
end

I get this error:

TypeError: can’t convert StringIO into String  
from c:/ruby/lib/ruby/1.8/net/ftp.rb:571:in `open' from c:/ruby/lib/ruby/1.8/net/ftp.rb:571:in `puttextfile’  
from (irb):33  
from c:/ruby/lib/ruby/1.8/net/ftp.rb:115:in `open’  
from (irb):32

Obviously it wants a filename, and I’m giving it the actual IO object  
instead. .NET had the concept of IO streams, and I was hoping to use  
something like that here, but I can’t figure out how.

Any ideas?

Thanks!  
Jeff

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [April 4, 2007, 4:58am UTC](https://www.ruby-forum.com/t/net-ftp-puttextfile-question/91051/2 "2007-04-04T04:58:36Z")

</div>

On Apr 3, 8:26 pm, “Jeff” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> &block)
> 
> but I don’t have a local filename to provide. Is there anything I can  
> use that would substitute for one?

I ended up subclassing and providing the functionality myself.

In case it helps anyone, here’s what I came up with: two methods,  
get\_text\_lines and send\_text\_lines. See below for complete source  
(it’s not too long). Suggestions for making the code more elegant are  
definitely welcome.  
Jeff

require ‘net/ftp’

class JeffFTP \< Net::FTP

# Sends a collection of lines to be saved in a file on the server

# lines can be an array, or anything responding to #each

def send\_text\_lines(lines, remotefile, &block) # :yield: line  
storlines\_from\_array("STOR " + remotefile, lines, &block)  
end

# Gets a file from the server as a collection of text lines.

# If you don’t provide a block, it will return an array of lines.

# If you provide a block, each line will be passed to the block,

# and the return value will be an empty array.

# Large files should use the block form.

def get\_text\_lines(remotefile, &block) # :yield: line  
lines = []  
retrlines("RETR " + remotefile) do |line|  
if block  
yield(line)  
else  
lines \<\< line  
end  
end  
lines  
end

private

def storlines\_from\_array(cmd, lines, &block) # :yield: line  
synchronize do  
voidcmd(“TYPE A”)  
conn = transfercmd(cmd)  
lines.each do |line|  
conn.write(line)  
yield(line) if block  
end  
conn.close  
voidresp  
end  
end

end

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [April 6, 2007, 3:32pm UTC](https://www.ruby-forum.com/t/net-ftp-puttextfile-question/91051/3 "2007-04-06T15:32:43Z")

</div>

On 4/3/07, Jeff [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> &block)
> 
> ```
> from c:/ruby/lib/ruby/1.8/net/ftp.rb:115:in `open'
> from (irb):32
> 
> ```
> 
> Obviously it wants a filename, and I’m giving it the actual IO object  
> instead. .NET had the concept of IO streams, and I was hoping to use  
> something like that here, but I can’t figure out how.
> 
> Any ideas?

Try turning the problem around and look at using open-uri instead of  
Net::FTP? This extends Kernel::open so that it accepts ftp and http  
urls as well as file names. This makes the ftp server look like a  
local file.

So you’d do something like this (untested):

require ‘open-uri’

## open(“ftp://user:removed\_email\_address@domain.invalid/data/file.xml”,‘w’) do | f | # write your data to the IO object f here end

Rick DeNatale

My blog on Ruby  
[http://talklikeaduck.denhaven2.com/](http://talklikeaduck.denhaven2.com/)
