Alexander Spitsyn
Posted on September 18, 2019
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
Posted on September 18, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.