Snap框架提供了Snap.Util.FileUploads
module,但是非常不友好。我正在编写一个简单的HTTP服务器,我并不真正在乎它的文件大小限制,上载速率限制限制,担心寻找一些与操作系统无关的临时目录来放置上载的文件,编写多部分数据流处理器等等。
当然,如果需要,Snap可以为我提供这一详细信息,这是很好的,但是我不需要。更糟糕的是,有零个示例说明如何编写文件上传处理程序。我本来希望handleFileUploadsSimple :: MonadSnap snap => snap [File]
。像Scotty's files
action这样的东西,您猜对了,它给了我上传的文件bish-bash-bosh。
如果确实存在,它在哪里?如果没有,我应该怎么写?
最佳答案
这些政策是重要的安全功能;要他们。
政策规定
首先,您需要制定默认策略。我将使用默认的速率限制等,但将最大文件大小设置为2Mb,而不是默认的128Kb。
maxMb = 2
megaByte = 2^(20::Int)
myDefaultPolicy :: UploadPolicy
myDefaultPolicy = setMaximumFormInputSize (maxMb * megaByte) defaultUploadPolicy
接下来,我们需要一个按部就班的政策。您似乎不想检查有关上传的任何内容,我认为这很奇怪,但我们将保持2Mb的限制。我们将使用
allowWithMaximumSize :: Int64 -> PartUploadPolicy
函数:myPerPartPolicy :: PartInfo -> PartUploadPolicy
myPerPartPolicy _ = allowWithMaximumSize (maxMb * megaByte)
请记住,这就是Snap希望您看到的mime类型等,因为
data PartInfo =
PartInfo { partFieldName :: !ByteString
, partFileName :: !(Maybe ByteString)
, partContentType :: !ByteString
}
因此,您可以定义一个更智能的
myPerPartPolicy
来检查它进入的字段,并且该字段位于您的mime类型白名单中,而不是忽略信息。也许您正在计划这样做,因为Scotty的files动作在输出中而不是在处理程序中提供了这一点。上传处理程序
您需要实际的文件处理程序。此功能可完成上传内容。要求您将其编写为处理程序,而不是仅仅为您提供数据,这使您可以采用更静态,模块化的方式来编写代码,因此,与仅丢弃数据相比,它是一种优势。
myBusinessLogic :: MonadSnap m => PartInfo -> FilePath -> m ()
myBusinessLogic = undefined -- whatever you actually want to do with your uploads
警告:
用户处理程序运行之后(但在将响应主体枚举器流式传输到客户端之前),文件将从磁盘中删除;因此,如果您想在生成的响应中保留或使用上载的文件,则需要移动或进行其他处理。
让我们将其包装成一种可以忽略不正确上传的文件,并一次对一个有效的文件实施业务逻辑。请注意,我们将自己限制为不返回任何内容,因此我们可以使用
mapM_
一步一步地执行整个操作,但是如果您愿意,可以通过过滤掉不需要的内容来做一些更聪明的事情列表,并返回比()
更有趣的内容。无论如何,我都使用了过滤器来指向该方向:myUploadHandler :: MonadSnap m =>
[(PartInfo, Either PolicyViolationException FilePath)] -> m ()
myUploadHandler xs = mapM_ handleOne (filter wanted xs) where
wanted (_,Left _) = False
wanted (_,Right _) = True
handleOne (partInfo,Right filepath) = myBusinessLogic partInfo filepath
您无需担心与操作系统无关的临时文件文件夹,因为如果您使用
import System.Directory
,则可以使用getTemporaryDirectory :: IO FilePath
来获取临时目录。 (如果愿意,可以使用createDirectoryIfMissing:: Bool -> FilePath -> IO ()
和getAppUserDataDirectory :: String -> IO FilePath
在当前用户空间中使用特定于应用程序的目录。)无论如何,您都需要将从那里获得的文件路径传递给处理程序。全部一起
现在
handleFileUploads :: MonadSnap m =>
FilePath -> UploadPolicy -> (PartInfo -> PartUploadPolicy)
-> ([(PartInfo, Either PolicyViolationException FilePath)] -> m a)
-> m a
所以你可以写
myHandleFileUploads :: MonadSnap m => FilePath -> m ()
myHandleFileUploads tempDir =
handleFileUploads tempDir myDefaultPolicy myPerPartPolicy myUploadHandler
您仍然需要像我前面提到的那样从
tempDir
传递System.Directory
,但是该myHandleFileUploads
可以滚动了。