用流写二进制文件

时间:2018-04-11 23:09:57

标签: f#

如何通过临时目录中的流下载图像文件,我遵循代码并且我遇到了困难,需要使用搜索和计数部分进行指导。有一些包装器方法,但我正在寻找专门针对RAM效率原因的while循环方法。

编写

let tempFileName = Path.GetTempFileName()

let request = WebRequest.CreateHttp "http://example.com/image.png"
use response = request.GetResponse() :?> HttpWebResponse
use stream = response.GetResponseStream()

let buffer = Array.zeroCreate 1024
use reader = new BinaryReader(stream)

use memoryStream = new MemoryStream()
use fileStream = new FileStream(tempFileName, FileMode.Open)

while not (reader.PeekChar() <> -1) do
    fileStream.Write(reader.ReadBytes(1024), 0, 1024)

return Ok (tempFileName)

2 个答案:

答案 0 :(得分:2)

首先,我注意到虽然您正在创建buffer数组,但您实际上并未使用它。其次,当我查看BinaryReader documentation,特别是ReadBytes method的文档时,我注意到它需要一个int参数并返回一个字节数组。这必然意味着它每次都会分配一个新数组,这似乎与你想要的相反(因为你提到RAM效率,我认为你真正想要的是重新 - 每次使用相同的缓冲区。)

还有一个观察结果:ReadBytes方法表示,如果可用的字节数较少,它可能会返回一个小于请求大小的数组。您的代码目前尚未处理此案例。

但是,通过切换到BinaryReader.Read(byte[], int, int) method,可以修复所有这些问题。使用此方法,您的while循环将类似于以下内容:

while not (reader.PeekChar() <> -1) do
    let bytesRead = reader.Read(buffer, 0, 1024)
    fileStream.Write(buffer, 0, bytesRead)

现在我们正在跟踪每个Read操作读取的字节数,我们可以摆脱PeekChar调用并节省一些时间(调用{{1你下载的东西并非没有成本,因为库必须下载下一个字节,然后将其保存在某个地方,以便下次调用PeekChar时可以返回它。我们可以通过检查前一次调用读取的字节数来做到这一点:如果它是0,那么这意味着我们在流的末尾。为此,我们将Read变量移出循环,这意味着将其变为一个可变变量,我们每次循环都会重复使用它:

bytesRead

或者,如果您希望稍微更明确一点,如果您在bytesRead为0时跳过let mutable bytesRead = -1 while not (bytesRead = 0) do bytesRead <- reader.Read(buffer, 0, 1024) fileStream.Write(buffer, 0, bytesRead) ,则可以添加Write块:

if

最后let mutable bytesRead = -1 while not (bytesRead = 0) do bytesRead <- reader.Read(buffer, 0, 1024) if bytesRead > 0 then fileStream.Write(buffer, 0, bytesRead) 语句并非绝对必要,但是:if如果要求写0字节,则应该返回而不做任何事情。但是,由于我没有在任何可以找到的地方记录,我在最后一个代码示例中添加了FileStream.Write语句只是为了安全起见。

答案 1 :(得分:0)

从.NET 4.6.2开始,有 System.IO.Stream#CopyTo 方法:

namespace FSharpBasics

module ImageCrawler =

    open System.Net
    open System.IO
    open System.Text.RegularExpressions

    let private myurl = "https://cdn.pixabay.com/photo/2016/07/06/15/29/math-1500720_960_720.jpg"

    let crawler (url: string) =
        let fileName = Regex.Match(url, @"\/([^\/]+)$", RegexOptions.RightToLeft).Groups.[1].Value
        let request = WebRequest.CreateHttp url
        let response = request.GetResponse()
        use s = response.GetResponseStream()
        use w = File.Create fileName
        s.CopyTo w
        w.Flush true

    [<EntryPoint>]
    let main argv =
        printfn "JPEG file will be saved"
        crawler myurl
        printf "Saved"
        0