如何在接收 file/image 上传的 Yesod 中编写 JSON 端点?
How to write a JSON endpoint in Yesod which receives file/image uploads?
我看到很多帖子涉及编写表单小部件来处理图像上传,但我的 Yesod 服务器只是一个 JSON API。我将通过 Angular 文件上传接收 Post 请求。
一个简单的方法是将文件编码为 base64 数据,然后
然后将其作为 JSON 的一部分发送。但是这种方法的缺点是
它增加了数据大小。
示例:
#!/usr/bin/env stack
{- stack
--resolver lts-6.24
--install-ghc
runghc
--package yesod
--package yesod-core
--package persistent
--package text
--package aeson
--package bytestring
--package base64-bytestring
-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances#-}
{-# LANGUAGE OverloadedStrings#-}
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad (join)
import Control.Applicative
import Data.Text (Text, unpack)
import qualified Data.Text.Lazy.Encoding
import Data.Typeable (Typeable)
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import Yesod
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import qualified Data.ByteString.Base64 as BS
import qualified Data.Text.Encoding as T
data App = App
mkYesod
"App"
[parseRoutes|
/json/test TestR POST
|]
instance Yesod App where
approot = ApprootStatic "http://localhost:3006"
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
data Test = Test {
fileData :: Text,
name :: String
} deriving (Show, Eq, Ord)
instance FromJSON Test where
parseJSON (Object v) = Test <$>
v .: "fileData" <*>
v .: "name"
parseJSON _ = empty
postTestR :: Handler TypedContent
postTestR = do
testData :: Test <- requireJsonBody
let fileData' = BS.decode (T.encodeUtf8 $ fileData testData)
case fileData' of
Left err -> error err
Right dat -> liftIO $ BS.writeFile "/home/sibi/myfile" dat
selectRep $ provideRep $ return emptyObject
main :: IO ()
main = warp 3006 App
执行时:
$ curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"fileData":"aGVsbG8gd29ybGQ=","name":"Filename"}' http://127.0.0.1:3006/json/test
$ cat /home/sibi/myfile
hello world
我看到很多帖子涉及编写表单小部件来处理图像上传,但我的 Yesod 服务器只是一个 JSON API。我将通过 Angular 文件上传接收 Post 请求。
一个简单的方法是将文件编码为 base64 数据,然后 然后将其作为 JSON 的一部分发送。但是这种方法的缺点是 它增加了数据大小。
示例:
#!/usr/bin/env stack
{- stack
--resolver lts-6.24
--install-ghc
runghc
--package yesod
--package yesod-core
--package persistent
--package text
--package aeson
--package bytestring
--package base64-bytestring
-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances#-}
{-# LANGUAGE OverloadedStrings#-}
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad (join)
import Control.Applicative
import Data.Text (Text, unpack)
import qualified Data.Text.Lazy.Encoding
import Data.Typeable (Typeable)
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import Yesod
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import qualified Data.ByteString.Base64 as BS
import qualified Data.Text.Encoding as T
data App = App
mkYesod
"App"
[parseRoutes|
/json/test TestR POST
|]
instance Yesod App where
approot = ApprootStatic "http://localhost:3006"
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
data Test = Test {
fileData :: Text,
name :: String
} deriving (Show, Eq, Ord)
instance FromJSON Test where
parseJSON (Object v) = Test <$>
v .: "fileData" <*>
v .: "name"
parseJSON _ = empty
postTestR :: Handler TypedContent
postTestR = do
testData :: Test <- requireJsonBody
let fileData' = BS.decode (T.encodeUtf8 $ fileData testData)
case fileData' of
Left err -> error err
Right dat -> liftIO $ BS.writeFile "/home/sibi/myfile" dat
selectRep $ provideRep $ return emptyObject
main :: IO ()
main = warp 3006 App
执行时:
$ curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"fileData":"aGVsbG8gd29ybGQ=","name":"Filename"}' http://127.0.0.1:3006/json/test
$ cat /home/sibi/myfile
hello world