[英]Streaming bytestring as WAI HTTP server response body
我有一個基於BS.readFile
的函數的值body :: BS.ByteString (ResourceT IO) ()
。 我想將該值作為Wai Application
的響應主體進行流式處理。 有一個幫助器streamingResponse
,它采用Stream (Of ByteString) IO r
類型的值。 我能到我的轉換BS.ByteString (ResourceT IO) ()
以Stream (Of ByteString) (ResourceT IO) ()
通過使用BS.toChunks
,但它包含一個額外的ResourceT
單子層。 將body
傳遞給streamingResponse
給我:
Couldn't match type ‘ResourceT IO’ with ‘IO’
Expected type: Stream (Of ByteString) IO ()
Actual type: Stream (Of ByteString) (ResourceT IO) ()
我已經嘗試過各種方法,例如將它們包裝在runResourceT中,綁定和提升值等,但實際上不知道如何進行。 這里的在整個項目的行,如果需要額外的上下文。
hoist runResourceT body
似乎要進行類型檢查。 有人還向我介紹了Haskell Pipes 線程 ,這可能是一個非常相關的問題,並且可能提示解決方案。
代替readFile
, withFile
+ hSetBinaryMode
+ Data.ByteString.Streaming.fromHandle
是否足夠?
fromHandle
產生一個ByteString IO ()
,可以將其轉換為streamingResponse
或streamingBody
可以接受的Stream (Of ByteString) IO ()
。
存在將withFile
包圍操作放在何處的問題。 根據WAI文檔 ,您可以將Application
構建功能的結果包裝在一起:
請注意,從WAI 3.0開始,此類型以連續傳遞樣式構造,以允許正確安全地處理資源。 過去是通過其他方式(例如ResourceT)來處理的。
[...]
為了以異常安全的方式分配資源,可以在對responseStream的調用之外使用括號模式。
注意: streaming- fromHandle
文檔指出,到達EOF時fromHandle
自動關閉句柄。 看一下實現, 情況似乎並非如此 。 您需要withFile
才能正確關閉句柄。
如果我們想允許Stream
在ResourceT
,則可以不使用stream-wai的功能(僅適用於基於IO
Stream
),而可以在諸如network-wai的 responseStream
之類的功能之上構建:
import Control.Monad.Trans.Resource
import Network.Wai
import Streaming
import qualified Streaming.Prelude as S
import Data.ByteString.Builder (byteString, Builder)
streamingResponseR :: Stream (Of ByteString) (ResourceT IO) r
-> Status
-> ResponseHeaders
-> Response
streamingResponseR stream status headers =
responseStream status headers streamingBody
where
streamingBody writeBuilder flush =
let writer a =
do liftIO (writeBuilder (byteString a))
-- flushes for every produced bytestring, perhaps not optimal
liftIO flush
in runResourceT $ void $ S.effects $ S.for stream writer
streamingBody
具有類型StreamingBody
,這實際上是一個功能的類型的同義詞(Builder -> IO ()) -> IO () -> IO ()
需要一個寫入回調和沖洗回調作為參數,並使用它們來寫使用范圍內的某些數據源進行響應。 (請注意,這些回調是由WAI提供的 ,而不是由用戶提供的。)
在我們的例子中,數據源是Stream
在ResourceT
的Stream
。 我們需要使用liftIO
來提升write和flush回調(位於IO
),還要記住調用runResourceT
以在最后返回簡單的IO
操作。
如果我們想沖洗后, 才發出的字節串的累計長度達到一定極限的反應如何?
每次達到限制時,我們將需要一個函數(此處未實現)來創建除法:
breaks' :: Monad m
=> Int
-> Stream (Of ByteString) m r
-> Stream (Stream (Of ByteString) m) m r
breaks' breakSize = undefined
然后,我們可以在編寫流之前使用intercalates
在每組之間插入刷新動作:
streamingBodyFrom :: Stream (Of ByteString) (ResourceT IO) ()
-> Int
-> StreamingBody
streamingBodyFrom stream breakSize writeBuilder flush =
let writer a = liftIO (writeBuilder (byteString a))
flusher = liftIO flush
broken = breaks' breakSize stream
in runResourceT . S.mapM_ writer . S.intercalates flusher $ broken
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.