Can the Sketchup::Http
module be used to asynchronously download files?
It looks like so considering the set_download_progress_callback
method.
But where is the file stored once downloaded?
Is there more information somewhere?
Can the Sketchup::Http
module be used to asynchronously download files?
It looks like so considering the set_download_progress_callback
method.
But where is the file stored once downloaded?
Is there more information somewhere?
I think you get the response as a String. You should then be able to save it out anywhere you want it to go.
when dealing with large files
How large? The below seemed to work several times on a 10.3 MB file. Code isnāt quite what Iād use in an appā¦
# frozen_string_literal: true
module SUFileDownload
URI = 'https://github.com/MSP-Greg/ruby-loco/releases/download/ruby-master/ruby-mingw.7z'
FN = "#{__dir__}/ruby-mingw.7z"
class << self
def run
t_st = Time.now.to_f
req = Sketchup::Http::Request.new URI
req.start do |req, resp|
if req.status == Sketchup::Http::STATUS_SUCCESS
File.write FN, resp.body, mode: 'wb'
puts "Download time: %5.2f sec" % (Time.now.to_f - t_st)
else
puts "#{req.status} can't download"
end
end
end
end
end
SUFileDownload.run
As shown above, it is loaded as the response.body
.
The block parameters for this method have a bug (at least on Windows.)
I logged it privately, but I should create a public issue for it.
Basically the total
parameter is nil
or 0
(cannot remember which,) ā¦ making any comparison with the current
value (as to percent done downloading) a no-op.
So your code would need to know how big the file to be downloaded really is, before the download begins.
I played around with this a bit more. Used a 127MB file, and it downloaded ok with both Net::HTTP and Sketchup::Http. Both took about the same amount of time, and which was fastest varied. The file is hosted on AWS from a GitHub redirect, so the connection is solid (and also https).
But, Net::HTTP showed an hourglass, while Sketchup::Http did not.
Hence, if youāre having issues with Sketchup::Http, it may be the manner in which youāre calling it, as you mentioned.
I did find that https using Net::HTTP with OpenSSL <= 1.0.2 / Ruby < 2.5 / SU <= 2018 is a mess. I had a large model (30 MB) loaded, and the first connection took 22 sec, and that was downloading a small file. Thomas has mentioned this, and tracked the problem down a while ago.
Iām not sure how it could be done, but some way to run āsafeā Ruby code in a manner that didnāt āblockā SU would be helpful.
Off topic, but I ran the downloads with both mingw & mswin Ruby builds, no difference.
I guess so. I am not always having trouble with it, not at all. One of my larger extensions is having lots of issues with it. There is an update checker that does an http request. In the callback, if there is an update, it shows a UI::Notification, and, in the callback of that notification, which gets called if the user does press the agree button, , it does a second request to actually download the update. This chain gave me many headaches, which I probably assigned falsely to the http module.
I set a separate script to download the 130 MB file and then exit, and in SU spawned a process running it with rubyw.exe.
Then using a UI timer and Process.waitpid(pid, Process::WNOHANG)
in the timer block, waited for the download to finish. SU seemed to stay totally responsive during the download.
One might consider the spawned ruby process similar to a web worker, in that it canāt use any SU objects.
Regardless, having ruby executables included with SU would allow devs to expand on what can currently be done.
Love the idea to have these async web-worker-like ruby executables, which you could send a script to. I currently do more or less the same, but with .net executables that I spawn and check their pid in a timer. You can then also define a pool size, of lets say 5, and continously check which subprocesses are done and execute new tasks in parallel.
Maybe start another thread about this? Looks cool
If only communication using stdin stdout with a subprocess was not broken in SUā¦
Edit: I also removed my comments regarding the issues I experience with Sketchup:HTTP, it must be because I use it in some edge case, not really adding to the topic.
Thank you all for your input!
I donāt know how I didnāt think about that ^^
Actually the total
parameter is always -1
.
I looked up my previous report and see that it was internally logged on 1 OCT 2018 by Hilllard.
Iāve relogged in the public Issue tracker for all to see and track. Also updated the test script to v3.
Thatās not the experience Iām having.
The total is correct on SketchUp 2017 up to 2020.
In your case, does the response header contain the content size?
I just tested it this morning on 2020, and had tested it back to 2017 in the past.
Did you test with the test script I posted ?
Do you have another URL that I can run the test script on?
The response is not available in the block for the method that the issue is posted for.
I think you need to reread the posted issue. The current
parameter (which is a cumulative value) is correct, and will be the total value downloaded on the last call of the block, BUT ā¦ there is no way to know which block call is the last, because (as I wrote in the issue report) the request.status
is still STATUS_PENDING
.
Are you suggesting a workaround to firstly make a HEAD request, get the āContent-Lengthā and then make the actual GET request and use the size from response to the HEAD request ?
Tried this. See discussion:
Unfortunately, servers MAY (and often do) omit the āContent-Lengthā header in response to HEAD requests.
My test happens to request a zip from GitHub which isnāt usually returning a āContent-Lengthā for a HEAD request. (I did once get it to do so, but I donāt know what and why it did in that case. It was a bogus mistaken request header I think that triggered it.)
Thatās my point. If the server is not returning the Content-Length
, how could SketchUp know what itās supposed to be?
Well, I am operating under the principle (which Trimble employees keep stating) that the API documentation states ācontractsā for feature behavior.
In the case of the Sketchup::Http::Request#set_download_progress_callback
method, the docs state without any variance, that the total
block parameter is ā¦
total
(Integer
) ā Total bytes to transfer.
So, if the documentation is incorrect it needs to be corrected to say for example ā¦
When the request is a GET, and this method registers a callback block, the API will first send a HEAD request for an attempt to retrieve the āContent-Lengthā header for the
total
block parameter.
If the server at the request URL does not return this header perRFC2616:9.4
orRFC2616:14.13
, then the value of thetotal
block parameter will be-1
. Coders should write their callback code in an agile manner to account for situations where the total length of the download is unknown.
Iāve updated the tracker Issue with comments to this effect.
Still seeking a test URL for a server that will return a "Content-Length"
header in response to a HEAD request, with a "Access-Control-Request-Method": "GET"
header.
If you want a progress indicator and can live with a sync (blocking download), the attached file shows an example using Net::HTTP. It just outputs the percent and āstepā to the console approx every secondā¦
EDIT: first copy of the file I uploaded had an error in it, as I mistakenly left io.write chunk
inside a conditional it shouldnāt have been inside. I added the conditional to ālightenā the logging of the download progressā¦
download-test_su-http.rb (3.0 KB)
Well, yes, Iāve used Net::HTTP
in the past (especially before the SketchUp API added Sketchup::Http
classes.)
But ā¦ this, (knowing the download size in order to calculate percentage,) is kind of more about the SketchUp API and itās documentation.
But to humor you, how would the Standard libraries deal with the same issue ?
Ie, a server that does not return a "Content-Length"
header to a HEAD request ?
But to humor you
Really?
Well, yes, Iāve used
Net::HTTP
in the past
Apparently not enough.
Ie, a server that does not return a
"Content-Length"
header to a HEAD request
Look at the code I attached. With Sketchup::Http
, the headers are only available after the full response body has been retrieved. So you thought doing a HEAD request was a possible solution.
HTTP doesnāt work that way, thatās only the SU implementation. With Net::HTTP
, the headers are available immediately, and most downloads of any size are chunked. Hence, one can calculate the percent completed. There are too many chunks to have SU respond for each, so thatās why the code only outputs approx every secondā¦
Ouch! Iāll admit itās been about a year since. Thanks for ārubbinā it inā.
I am (and am about to test it out.)
I know this, and perhaps this is part of the problem with the API download and upload callback block form methods. (Ie, ā¦ they ādumbed these classes downā too much. It might be beneficial if the chunked response headers were available as a block parameter for the these methods.)
A workaround actually. I think that a GET Sketchup::Http::Request
might actually do this āunder the hoodā so this is why I tried it. It has helped us to understand what is going on and that code (in these API callback blocks) needs to be a bit more agile.
Again, I realize this (after spending more than 3 days playing and reading RFCs.)
The SketchUp API has wrapped up a bunch of implementation, but poorly documented what to expect when using these methods. I feel that the coders of this implementation must have known that the download total
parameter would remain as initialized at -1
if the "Content-Length"
header was not present, but did not document this in the API dictionary.
One simple short line of explanation in the docs could have saved me from wasting at least 3 days (probably more) on this issue.