32

I'm trying to make a route to a specific static file but everything I'm trying ends with an error.

I've made 3 different attempts:

1.

GET /file   staticFile:/public/html/file.html

The error I get:

Compilation error
string matching regex `\z' expected but `:' found

2.

GET /file   controllers.Assets.at(path="/public/html", "file.html")

The error I get:

Compilation error
Identifier expected

3.

GET /file   controllers.Assets.at(path="/public/html", file="file.html")

The error I get: (and this is the weirdest)

Compilation error
not enough arguments for method at: (path: String, file: String)play.api.mvc.Call. Unspecified value parameter file.

The weird part about the 3rd error is that it's thrown in a different file (app/views/main.scala.html) on the following line:

<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">

All of these methods were found in the official documentation and/or threads here on stackoverflow. What am I missing here?

Thanks.

flurdy
  • 3,782
  • 29
  • 31
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 1
    I think the 3rd error occurs where the first instance of public routing is mentioned in the default templates. I have the exact same issue. I expect the static link you/I try to add messes up the reverse routing. – flurdy Mar 31 '12 at 19:14
  • What? two years and there is no accepted answer. Has this problem been solved yet ? – Jus12 Oct 05 '14 at 16:19
  • @Jus12 Sorry, it's just that I haven't continued using play (not by my choice, and it's unfortunate) and I don't know which answer is correct. The one with most votes did not solve the issue back then (as my comment explains). If someone will let me know if any of the answers is correct, I'll gladly mark it as accepted. – Nitzan Tomer Oct 05 '14 at 17:10
  • @NitzanTomer The [answer by Michael Allen](http://stackoverflow.com/a/18236129/243233) worked for me. – Jus12 Oct 05 '14 at 17:46
  • 1
    @Jus12 As I'm not sure how to go about this, I asked in the meta site for recommendations, looks like it should be left as it is: http://meta.stackoverflow.com/questions/272882/how-to-know-which-answer-to-accept-when-you-dont-know-which-one-is-the-correct?noredirect=1#comment101897_272882 – Nitzan Tomer Oct 06 '14 at 19:55

9 Answers9

19

IIRC, change

<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">

To

<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/", "main.css")">

I am talking about your third attempt

Also, watch out for extra /

EDIT

GET /assets/main.css    controllers.Assets.at(path="/public", file="/stylesheets/main.css")

Assuming your resource is at /public/stylesheets/main.css

Jamil
  • 2,150
  • 1
  • 19
  • 20
  • 2
    This just resolves in another error, but what you changed is not the problem, when I comment out this routing line all works well (except that the file is not served). The error in the third attempt is of the main template file and that is exactly what I got from the _play new app-name_ command, I did not change it, and as I wrote, it works well without that routing. – Nitzan Tomer Mar 21 '12 at 21:13
13

I am not totally sure if this is correct, but this is what we are using to map a public folder containing our images, javascripts... etc..

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)
Nick
  • 1,174
  • 11
  • 20
  • Thanks for the reply, I had no way of trying this so far, I will hopefully get back to it and will report back. – Nitzan Tomer Aug 02 '12 at 08:11
  • If I rename the "public" folder to be "assets", this doesn't seem to work (using `path="/assets"`,...). Any idea why? – Jon Onstott Oct 14 '14 at 02:38
13

This ability still haven't been added, as I know. But if someone needs answer, as an option, helper controller can be created for these purpose:

object StaticFile extends Controller {

  def html(file: String) = Action {
    var f = new File(file)

    if (f.exists())
      Ok(scala.io.Source.fromFile(f.getCanonicalPath()).mkString).as("text/html");
    else
      NotFound
  }

}

and then in routes config

GET     /               controllers.StaticFile.html(file = "public/index.html")
Andrey Antonov
  • 131
  • 1
  • 5
10

The solution to this that is cleanest is to create your own AssetsBuilder that will build your static page.

Create this file in your controllers package - Static.scala

package controllers

object Static extends AssetsBuilder

Then in your routes you can define your static endpoint

GET /file   controllers.Static.at(path="/public/html", "file.html")

Done. Now the file at /public/html/file.html will be served off of localhost:9000/file

If you replace the hard code above with a more generic:

GET /*file   controllers.Static.at(path="/public/html", *file + ".html")

then /foo will serve /public/html/foo.html, /bar will serve /public/html/bar.html etc.

Michael Allen
  • 5,712
  • 3
  • 38
  • 63
5

Your third attempt was almost right. Instead of

GET /file   controllers.Assets.at(path="/public/html", file="file.html")

do it like this

GET /file   controllers.Assets.at(path="/public", file="html/file.html")

I got the same issue before. My route file looks like this.

# Application
GET /       controllers.Assets.at(path="/public/html", file="static.html")
GET /exmpl  controllers.Examples.index

# Resources
GET /assets/*file  controllers.Assets.at(path="/public", file)

And I have below reverse route inside views (examples.scala.html)

@routes.Assets.at("imagefolder")   #try to generate path to /public/imagefolder.

When I open http://localhost:9000/exmpl, this error showed up.

not enough arguments for method at: (path: String, file: String)play.api.mvc.Call. Unspecified value parameter file.

To fix this issue, I just changed this route

GET /     controllers.Assets.at(path="/public/html", file="static.html")

to this

GET /     controllers.Assets.at(path="/public", file="html/static.html")

This solution was works for me. I hope it works for you and the others too.

user2737506
  • 51
  • 1
  • 1
4

I have the exact same issue. I followed the advice from @Jamil and managed to get this working for the static file (in my case a favicon) and managed to get the templates to compile but get a new error at runtime when trying to use the view. Relevant code below,

Change to the route (this route now resolves correctly)

GET     /favicon.ico                controllers.Assets.at(path="/public/images", file="favicon.png")

Change to the view (compiles)

<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/","main.css")">

New error (only at runtime)

[MatchError: (stylesheets/,main.css) (of class scala.Tuple2)]

// @LINE:29
// @LINE:28
def at(path:String, file:String) = {
   (path, file) match {
// @LINE:28
case (path, file) if path == "/public" => Call("GET", "/assets/" + implicitly[PathBindable[String]].unbind("file", file))

I know this isnt an answer but perhaps it will allow someone to pipe up with one.

Ben Boyter
  • 879
  • 11
  • 16
  • Thanks for the reply, I had no way of trying this so far, I will hopefully get back to it and will report back. – Nitzan Tomer Aug 02 '12 at 08:11
  • 2
    The correct route is this: "GET /favicon.ico controllers.Assets.at(path="/public", file="images/favicon.ico")" – AlexV Dec 31 '13 at 04:56
3

I was experiencing the same issue while I was trying to configure some additional css. It worked with this syntax in the "routes" file

  GET   /css/*file                      controllers.Assets.at(path="/public/css", file)
loic23
  • 31
  • 1
1

I ran into the same issue. When I added a second controllers.Assets.at to routes like

GET  /assets/*file   controllers.Assets.at(path="/public", file)
GET  /assets2/*file  controllers.Assets.at(path="/public2", file)

the default @routes.Assets.at calls in main.scala.html failed compilation

Compilation error
not enough arguments for method at: (path: String, file: String)play.api.mvc.Call. Unspecified value parameter file.

This would seems to be because of an implicit parameter for which there are multiple matching objects. The solution is to add a leading positional parameter:

href="@routes.Assets.at("/public","/css/main.css")"

Unfortunately, if you go back to having only one assets line in routes, you will have to change to the one-parameter form to avoid a compilation error. Seems like a bug to me.

Eric Drechsel
  • 2,694
  • 2
  • 23
  • 24
1

Play java packages public folder in a jar file and this would be a problem if you would like to serve a static file that resolves to an absolute file location in the server. The right way to do is to have your own controller and use it to serve the static file.

For ex, to serve a file "mystatic.html" that is in the

<your playhome>
    |-----<myfolder>
        |------mystatic.html

You would configure this route in your routes file.

GET /mystatic           mypackage.mycontrollers.Static.getFile(path="/myfolder/mystatic.html")

and your controller would be implemented as follows.

package mypackage.mycontroller;

import java.io.File;
import com.google.inject.Inject;
import com.google.inject.Provider;

import play.Application;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Results;

public class Static extends Controller {

    @Inject
    Provider<Application> app;

    public Result getFile(String path){
        File file = app.get().getFile(path);
        if(file.exists()){
            return ok(file);            
        }else{
            return Results.notFound();
        }
    }   
}
Droidman
  • 540
  • 4
  • 7