2017-06-23 4 views
2

In unteren Code habe ich ein Bild im statischen Ordner: bsd.jpg. In der Ajax-Erfolgsmethode möchte ich die Img-Quelle auf ein statisches Bild zeigen und es zeigen. Ich konnte nicht herausfinden, warum ich diesen Code ausführen und eine Fehlermeldung erhalten:Yesod statische Dateityp sichere Route Variable nicht im Bereich

upload.hs:91:22: error: Variable not in scope: bsd_jpg :: Route Static 

#!/usr/bin/env stack 
{- stack 
    --resolver lts-8.19 
    --install-ghc 
    runghc 
    --package yesod 
    --package yesod-static 
    --package persistent-sqlite 
-} 

{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE QuasiQuotes #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE GADTs #-} 
{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
{-# LANGUAGE ViewPatterns #-} 

import Yesod 
import Yesod.Static 
import Data.Time (UTCTime) 
import System.FilePath 
import System.Directory (removeFile, doesFileExist, createDirectoryIfMissing) 
import Control.Applicative ((<$>), (<*>)) 
import Control.Monad.Logger (runStdoutLoggingT) 
import Data.Conduit 
import qualified Data.Text as T 
import Data.Text (unpack) 
import qualified Data.ByteString.Lazy as DBL 
import Data.Conduit.List (consume) 
import Database.Persist 
import Database.Persist.Sqlite 
import Data.Time (getCurrentTime) 
import qualified Data.Conduit.Text as CT 
import qualified Data.Conduit.List as CL 

share [mkPersist sqlSettings,mkMigrate "migrateAll"] [persistUpperCase| 
Image 
    filename String 
    description Textarea Maybe 
    date UTCTime 
    deriving Show 
|] 

data App = App 
    { getStatic :: Static --^Settings for static file serving. 
    , connPool :: ConnectionPool 
    } 

mkYesod "App" [parseRoutes| 
/ImagesR GET POST 
/image/#ImageId ImageR DELETE 
/static StaticR Static getStatic 
/echo-body EchoBodyR PUT 
|] 

instance Yesod App where 
    maximumContentLength _ (Just ImagesR) = Just $ 200 * 1024 * 1024 -- 200 megabytes 
    maximumContentLength _ _ = Just $ 10 * 1024 * 1024 -- 10 megabytes 

instance YesodPersist App where 
    type YesodPersistBackend App = SqlBackend 
    runDB action = do 
     App _ pool <- getYesod 
     runSqlPool action pool 

instance RenderMessage App FormMessage where 
    renderMessage _ _ = defaultFormMessage 

uploadDirectory :: FilePath 
uploadDirectory = "static" 

uploadForm :: Html -> MForm Handler (FormResult (FileInfo, Maybe Textarea, UTCTime), Widget) 
uploadForm = renderBootstrap $ (,,) 
    <$> fileAFormReq "Image file" 
    <*> aopt textareaField "Image description" Nothing 
    <*> lift (liftIO getCurrentTime) 

addStyle :: Widget 
addStyle = do 
    -- Twitter Bootstrap 
    addStylesheetRemote "http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.0/css/bootstrap-combined.min.css" 
    addStylesheetRemote "https://www.w3schools.com/w3css/4/w3.css" 
    -- message style 
    toWidget [lucius|.message { padding: 10px 0; background: #ffffed; } |] 
    -- jQuery 
    addScriptRemote "http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" 
    -- delete function 
    toWidget [julius| 
$(function(){ 
    function confirmDelete(link) { 
     if (confirm("Are you sure you want to delete this image?")) { 
      deleteImage(link); 
     }; 
    } 
    function deleteImage(link) { 
     $.ajax({ 
      type: "DELETE", 
      url: link.attr("data-img-url"), 
     }).done(function(msg) { 
      var table = link.closest("table"); 
      link.closest("tr").remove(); 
      var rowCount = $("td", table).length; 
      if (rowCount === 0) { 
       table.remove(); 
      } 
     }); 
    } 

    $("#screenshot").click(function(){ 
    $.ajax({ 
      // sending a JSON encoded body 
      contentType: "application/json", 
      // don't process the body, we'll render data into a valid string 
      processData: false, 
      url: "@{EchoBodyR}", 
      type: "PUT", 
      // notice the usage of stringify here 
      data: JSON.stringify([{name:"Alice",age:25}, {name:"Bob",age:30}]), 
      success: function(data) { 
       alert(data.body); 
       $("#screenshot-pic").attr("src","@{StaticR bsd_jpg}"); 

      }, 
      // this only refers to the data type of the *returned* data 
      dataType: "json" 
     }); 

    return false; 
    }); 

    $("a.delete").click(function() { 
     confirmDelete($(this)); 
     return false; 
    }); 
}); 
|] 
putEchoBodyR :: Handler Value 
putEchoBodyR = do 
    texts <- rawRequestBody $$ CT.decode CT.utf8 =$ CL.consume 
    return $ object ["body" .= T.concat texts] 


getImagesR :: Handler Html 
getImagesR = do 
    ((_, widget), enctype) <- runFormPost uploadForm 
    images <- runDB $ selectList [ImageFilename !=. ""] [Desc ImageDate] 
    mmsg <- getMessage 
    defaultLayout $ do 
     addStyle 
     [whamlet|$newline never 
$maybe msg <- mmsg 
    <div .message> 
     <div .container> 
      #{msg} 
<div .container> 
    <div .row> 
     <h2> 
      Upload new image 

     <button #screenshot class="w3-button w3-teal">Screenshot 

     <img #screenshot-pic class="w3-round"> 

     <div .form-actions> 
      <form method=post enctype=#{enctype}> 
       ^{widget} 
       <input .btn type=submit value="Upload"> 
     $if not $ null images 
      <table .table> 
       <tr> 
        <th> 
         Image 
        <th> 
         Decription 
        <th> 
         Uploaded 
        <th> 
         Action 
       $forall Entity imageId image <- images 
        <tr> 
         <td> 
          <a href=#{imageFilePath $ imageFilename image}> 
           #{imageFilename image} 
         <td> 
          $maybe description <- imageDescription image 
           #{description} 
         <td> 
          #{show $ imageDate image} 
         <td> 
          <a href=# .delete [email protected]{ImageR imageId}> 
           delete 

|] 

postImagesR :: Handler Html 
postImagesR = do 
    ((result, widget), enctype) <- runFormPost uploadForm 
    case result of 
     FormSuccess (file, info, date) -> do 
      -- TODO: check if image already exists 
      -- save to image directory 
      filename <- writeToServer file 
      _ <- runDB $ insert (Image filename info date) 
      setMessage "Image saved" 
      redirect ImagesR 
     _ -> do 
      setMessage "Something went wrong" 
      redirect ImagesR 

deleteImageR :: ImageId -> Handler() 
deleteImageR imageId = do 
    image <- runDB $ get404 imageId 
    let filename = imageFilename image 
     path = imageFilePath filename 
    liftIO $ removeFile path 
    -- only delete from database if file has been removed from server 
    stillExists <- liftIO $ doesFileExist path 

    case (not stillExists) of 
     False -> redirect ImagesR 
     True -> do 
      runDB $ delete imageId 
      setMessage "Image has been deleted." 
      redirect ImagesR 

writeToServer :: FileInfo -> Handler FilePath 
writeToServer file = do 
    let filename = unpack $ fileName file 
     path = imageFilePath filename 
    liftIO $ fileMove file path 
    return filename 

imageFilePath :: String -> FilePath 
imageFilePath f = uploadDirectory </> f 

openConnectionCount :: Int 
openConnectionCount = 10 

main :: IO() 
main = do 
    pool <- runStdoutLoggingT $ createSqlitePool "images.db3" openConnectionCount 
    runSqlPool (runMigration migrateAll) pool 
    -- Get the static subsite, as well as the settings it is based on 
    createDirectoryIfMissing True uploadDirectory 
    [email protected](Static settings) <- static uploadDirectory 
    warp 3000 $ App static pool 
+3

Dieses Beispiel ist bei weitem nicht minimal, Sie sollten versuchen, es zu reduzieren. Vermutlich (ich habe nicht den ganzen Code angeschaut) hast du nichts Dummes getan, wie zum Beispiel die Variable 'bsd_jpg' eingeben, ohne sie irgendwo zu definieren, in welchem ​​Fall deine Spleiße schlechten Code erzeugen. Verwenden Sie "-ddump-splices", um die tatsächliche Ausgabe der Spleiße zu sehen, und suchen Sie nach Vorkommen von 'bsd_jpg'. Meine Vermutung ist der Spleiß mit "@ {StaticR bsd_jpg}" ist der Schuldige. – user2407038

Antwort

3

Ich sehe nicht, wo du

import Yesod.Static (staticFiles) 

-- This generates easy references to files in the static directory at compile time, 
-- giving you compile-time verification that referenced files exist. 
-- Warning: any files added to your static directory during run-time can't be 
-- accessed this way. You'll have to use their FilePath or URL to access them. 
-- 
-- For example, to refer to @static/js/[email protected] via an identifier, you'd use: 
-- 
--  js_script_js 
-- 
-- If the identifier is not available, you may use: 
-- 
--  StaticFile ["js", "script.js"] [] 

staticFiles (appStaticDir compileTimeAppSettings) 

tun Dieser Code ist aus Standard-SQLite-basierten Yesod Gerüst genommen wird. Ich würde Ihnen empfehlen, damit zu beginnen (stack templates + stack new <selected-template>), wenn Sie gerade anfangen, mit Jessod zu spielen.

Beachten Sie, dass staticFiles eine Template-Haskell-Funktion ist und bei deren Verwendung einige Einschränkungen bestehen. Insbesondere können Sie generierten Code nicht aus der gleichen .hs-Datei verwenden, aus der er generiert wurde.

+0

wow wirklich! Ich habe keine Ahnung von der Beschränkung von Template Haskell – WellTyped