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可以滚动了。

10-06 04:34
查看更多