2012-06-04 24 views
12

Deseo implementar la carga de archivos grandes con mi aplicación Yesod. Ahora mismo tengo:Carga eficiente de archivos grandes con Yesod

module Handler.File where 

import Import 

import System.Random 
import System.FilePath 
import Control.Monad 
import qualified Data.ByteString.Lazy as LBS 
import qualified Data.Text.Encoding 

-- upload 

uploadDirectory :: FilePath -- FIXME: make this configurable 
uploadDirectory = "incoming" 

randomFileName :: IO FilePath 
randomFileName = do 
    fname'base <- replicateM 20 (randomRIO ('a','z')) 
    let fname = uploadDirectory </> fname'base <.> "bin" 
    return fname 

fileUploadForm :: Form (FileInfo, Textarea) 
fileUploadForm = renderDivs $ (,) 
    <$> fileAFormReq "Choose a file" 
    <*> areq textareaField "What's on the file?" Nothing 

getFileNewR :: Handler RepHtml 
getFileNewR = do 
    (formWidget, formEnctype) <- generateFormPost fileUploadForm 
    defaultLayout $ do 
     setTitle "Upload new file." 
     $(widgetFile "file-new") 

postFileNewR :: Handler RepHtml 
postFileNewR = do 
    user <- requireAuth 
    ((result, formWidget), formEnctype) <- runFormPost fileUploadForm 
    case result of 
    FormSuccess (fi,info) -> do 
       fn <- liftIO randomFileName 
       liftIO (LBS.writeFile fn (fileContent fi)) 
       let newFile = File (entityKey user) fn info (fileName fi) (fileContentType fi) 
       fid <- runDB $ insert newFile 
       redirect (FileViewR fid) 
    _ -> return() 

    defaultLayout $ do 
     setTitle "Upload new file." 
     $(widgetFile "file-new") 

Es sobre todo muy bien, a excepción de algunos problemas:

  1. tamaño máximo de un archivo es de alrededor de 2 megabytes. Tengo una solución, pero ¿es la forma correcta de hacer esto? Mi solución es reemplazar la implementación predeterminada del método maximumContentLength en Yesod ejemplo para mi aplicación, así:

    maximumContentLength _ (Just (FileNewR _)) = 2 * 1024 * 1024 * 1024 - 2 gigabytes maximumContentLength _ _ = 2 * 1024 * 1024 - 2 megabytes

  2. La cantidad de memoria utilizada es igual al tamaño de un archivo. Esto es realmente subóptimo. Me gustaría utilizar tempFileBackEnd desde http://hackage.haskell.org/packages/archive/wai-extra/1.2.0.4/doc/html/Network-Wai-Parse.html, pero no tengo idea de cómo conectarlo en mi solicitud y hacerlo funcionar con lógica de formularios (campo _token oculto, etc.).

  3. La carga es 'disparo único': ¿algún ejemplo de cómo hacer que funcione con cargadores basados ​​en Flash/Html5 que muestran el progreso al usuario?

Respuesta

11
  1. Su solución es correcta. El objetivo de la configuración maximumContentLength es permitirle anular este valor para rutas específicas que necesitan cargas más grandes.

  2. Esto es una deficiencia de la configuración actual de manejo de archivos en yesod-core. Actualmente está codificado para usar cargas de archivos en la memoria. Hemos discutido en la lista de correo en el pasado que esto no es óptimo. Acabo de crear un Github issue para esto, y la corrección se incluirá en Yesod 1.1 (sin embargo, no hay un cronograma en la versión).

  3. No tengo un ejemplo de esto, lo siento.

Cuestiones relacionadas