【问题标题】:Writing chunks of a large HTTP response to disk as soon as chunks arrive, in Squeak在 Squeak 中,一旦块到达,就将大型 HTTP 响应的块写入磁盘
【发布时间】:2026-02-12 20:40:01
【问题描述】:

我正在尝试将文件从 squeak 下载到磁盘。 我的方法适用于小文本/html文件, 但由于缺乏缓冲, 大型二进制文件非常慢 https://mirror.racket-lang.org/installers/6.12/racket-6.12-x86_64-win32.exe。 此外,完成后,文件要大得多(113 MB) 比下载页面上显示的 (75MB)。

我的代码如下所示:

download: anURL 
    "download a file over HTTP and save it to disk under a name extracted from url."
    | ios name |
    name := ((anURL findTokens: '/') removeLast findTokens: '?') removeFirst.
    ios := FileStream oldFileNamed: name.
    ios  nextPutAll: ((HTTPClient httpGetDocument: anURL) content).
    ios close.
    Transcript show: 'done'; cr.

我已经尝试[bytes = stream next bufSize. bytes printTo: ios] 使用[stream atEnd] whileFalse: 循环在HTTP 响应的contentStream 中使用[stream atEnd] whileFalse: 循环来处理固定大小的块,但这会使输出文件在每个块周围加上单引号,并且块之后还有额外的内容,看起来像流的所有字符,每个字符都用单引号引起来。

如何实现将 HTTP 响应缓冲写入磁盘文件? 另外,有没有办法在显示下载进度的同时发出吱吱声?

【问题讨论】:

  • 对于大小不匹配,您是否尝试将#binary 发送到FileStream,然后再存储二进制内容?
  • @Leandro 我曾想过这样做。但是我没有时间测试它。在我的电脑上开始下载超过五分钟后,吱吱声就冻结了。我也不确定关于代码的其他事情,所以在我学习了如何将 HTTP 响应逐块写入磁盘后,我决定这样做。

标签: http download stream smalltalk squeak


【解决方案1】:

正如 Leandro 已经写的那样,问题在于 #binary

您的代码几乎是正确的,我冒昧地运行它 - 现在它可以正确下载整个文件:

| ios name anURL |
anURL := ' https://mirror.racket-lang.org/installers/6.12/racket-6.12-x86_64-win32.exe'.
name := ((anURL findTokens: '/') removeLast findTokens: '?') removeFirst.
ios := FileStream newFileNamed: 'C:\Users\user\Downloads\_squeak\', name.
ios binary.
ios  nextPutAll: ((HTTPClient httpGetDocument: anURL) content).
ios close.
Transcript show: 'done'; cr.

至于冻结,我认为问题在于下载时整个环境的一个线程。这意味着在您下载整个文件之前,您将无法使用 Squeak。

刚刚在 Pharo 中进行了测试(安装更简单),以下代码可以按照您的需要运行:

ZnClient new
  url: 'https://mirror.racket-lang.org/installers/6.12/racket-6.12-x86_64-win32.exe';
  downloadTo: 'C:\Users\user\Downloads\_squeak'.

【讨论】:

  • 有没有办法在传入的 HTTP 块到达 squeak 时立即将它们写入磁盘?这样我就可以在下载发生时看到磁盘文件的大小发生变化。目前,这会下载整个文件几分钟,在此期间我可以看到高网络流量,随后是高 CPU 使用率,当它正在处理响应或写入磁盘时。
  • @pii_ke:@pii_ke:我认为最好在zn.stfx.eu/zn/index.html 使用Zinc HTTP 组件。 Squeak 4.2+ 不是 100%,但大部分都有效。
【解决方案2】:

WebResponse 类在构建响应内容时,会创建一个足够大的缓冲区来容纳整个响应,即使对于巨大的响应也是如此!我认为这是由于WebMessage>>#getContentWithProgress: 中的代码造成的。

我尝试将数据从WebResponse 的输入SocketStream 直接复制到输出FileStream。 我必须继承WebClientWebResponse,并编写两个方法。 现在以下代码按要求工作。

| client link |
client := PkWebClient new.
link := 'http://localhost:8000/racket-6.12-x86_64-linux.sh'.
client download: link toFile: '/home/yo/test'.

我已逐块验证下载文件的更新和完整性。

我在下面包含来源。方法streamContentDirectToFile: aFilePathString 是一种做事不同并解决问题的方法。

WebClient subclass: #PkWebClient
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PK'!
!PkWebClient commentStamp: 'pk 3/28/2018 20:16' prior: 0!
Trying to download http directly to file.!


!PkWebClient methodsFor: 'as yet unclassified' stamp: 'pk 3/29/2018 13:29'!
download: urlString toFile: aFilePathString 
    "Try to download large files sensibly"
    | res |
    res := self httpGet: urlString.
    res := PkWebResponse new copySameFrom: res.
    res streamContentDirectToFile: aFilePathString! !


WebResponse subclass: #PkWebResponse
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PK'!
!PkWebResponse commentStamp: 'pk 3/28/2018 20:49' prior: 0!
To make getContentwithProgress better.!
]style[(38)f1!


!PkWebResponse methodsFor: 'as yet unclassified' stamp: 'pk 3/29/2018 13:20'!
streamContentDirectToFile: aFilePathString 
    "stream response's content directly to file."
    | buffer ostream |
    stream binary.
    buffer := ByteArray new: 4096.
    ostream := FileStream oldFileNamed: aFilePathString.
    ostream binary.
    [stream atEnd]
        whileFalse: [buffer := stream nextInBuffer: 4096.
            stream receiveAvailableData.
            ostream nextPutAll: buffer].
    stream close.
    ostream close! !

【讨论】:

  • 出于好奇,您是否测量了不同缓冲区大小之间的差异?您的实现与 Zinc 之间的性能差异?您只是想重新实施还是有其他理由这样做?
最近更新 更多