Rewind Files Before Reading

spitsman

Alexander Spitsyn

Posted on September 18, 2019

Rewind Files Before Reading

Suppose you have a form with a file input and you need to forward that file to an external service through your Rails app. Params will look like this:

{ "file"=> #<ActionDispatch::Http::UploadedFile:0x00007fce9a8af140 @tempfile=#<Tempfile:/path/to/the/file.xlsx>, ... > }

The external service can not be reached for the first time sometimes, so you decided to set some retry count.

retry_count = 5
while retry_count > 0
    begin
        HTTPClient.new.post(url, params, headers)
    rescue ExternalServiceUnavailable => e
        retry_count -= 1
    end
end

The code above uses HTTPClient gem for making http requests and it will raise the following error in case there will be at least one retry:
ArgumentError: Illegal size value: #size returns 154139 but cannot read
The thing is that HTTPClient uses IO#read to read the file by chunks for making POST request. The lineno pointer reaches the end of the file after read, and you can not read bytes from the I/O stream anymore:

params['file'].tempfile.eof? # => true

To fix that, use IO#rewind – it positions IO to the beginning of input, resetting lineno to zero. Just add checking the file for eof before each request and rewind it if needed:

retry_count = 5
while retry_count > 0
    begin
        params['file'].tempfile.rewind if params['file'].tempfile.eof? # check eof
        HTTPClient.new.post(url, params, headers)
    rescue ExternalServiceUnavailable => e
        retry_count -= 1
    end
end
💖 💪 🙅 🚩
spitsman
Alexander Spitsyn

Posted on September 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related