далекое будущее Заголовок Expires для статического содержимого

как написал здесь http://developer.yahoo.com/performance/rules.html

Для статических компонентов: внедрите политику "Никогда не истекать", установив заголовок Expires в далеком будущем

я могу повысить производительность, избегая HTTP-запросов с ответом "304".
В официальной игре! документации я могу видеть, как установить директивы управления кешем, но как я могу установить заголовок Expires в далеком будущем?

С наилучшими пожеланиями Никола


изменить: спасибо за повтор, теперь он работает! вот классы:

конф/маршруты

  # Static files
  GET /assets/stylesheets/img/:name controllers.StaticFilesController.getBoostrapImg(name)
  GET /assets/images/*name          controllers.StaticFilesController.getImg(name)
  GET /assets/stylesheets/*name     controllers.StaticFilesController.getCss(name)
  GET /assets/javascripts/*name     controllers.StaticFilesController.getJs(name)


контроллеры/StaticFilesController.java

package controllers;
import org.apache.http.impl.cookie.DateUtils;
import java.util.*;
import play.mvc.*;
import services.FileName;
import play.*;
public class StaticFilesController extends Controller {

private static String nextYearString = StaticFilesController
        .getNextYearAsString();

public static Result getImg(String path) {

    FileName fileName = new FileName(path);
    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("image/" + fileName.extension());
    return ok(Play.application().getFile("/public/images/" + path));
}

public static Result getBoostrapImg(String path) {

    FileName fileName = new FileName(path);
    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("image/" + fileName.extension());
    return ok(Play.application().getFile(
            "/public/images/" + fileName.filename() + "."
                    + fileName.extension()));
}

public static Result getCss(String path) {

    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("text/css");
    return ok(Play.application().getFile("/public/stylesheets/" + path));
}

public static Result getJs(String path) {

    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("application/x-javascript");
    return ok(Play.application().getFile("/public/javascripts/" + path));
}

private static String getNextYearAsString() {
    Calendar calendar = new GregorianCalendar();
    calendar.add(Calendar.YEAR, 1);
    return DateUtils.formatDate(calendar.getTime());
}
}


services/ИмяФайла.java

package services;

/**
* This class assumes that the string used to initialize fullPath has a
* directory path, filename, and extension. The methods won't work if it
* doesn't.
*/
public class FileName {
private String fullPath;
private char pathSeparator, extensionSeparator;

public FileName(String str, char sep, char ext) {
 fullPath = str;
 pathSeparator = sep;
 extensionSeparator = ext;
}

public FileName(String str)
{
  fullPath = str;
  pathSeparator = '/';
  extensionSeparator = '.';
}

public String extension() {
  int dot = fullPath.lastIndexOf(extensionSeparator);
  return fullPath.substring(dot + 1);
 }

 public String filename() { // gets filename without extension
 int dot = fullPath.lastIndexOf(extensionSeparator);
 int sep = fullPath.lastIndexOf(pathSeparator);
  return fullPath.substring(sep + 1, dot);
}

 public String path() {
  int sep = fullPath.lastIndexOf(pathSeparator);
   return fullPath.substring(0, sep);
  }
 }


И views/main.scala.html

 @(skin: String)(content: Html)

 <!DOCTYPE html>

 <html lang="en">
   <head>
     <meta charset="utf-8"> 
     <title>LibreTitan</title>
      <link rel="stylesheet" media="screen"    href="@routes.StaticFilesController.getCss("bootstrap/bootstrap.min.css")">
    @if(skin != null && !skin.equals("")) {
    <link rel="stylesheet" media="screen" href="@routes.StaticFilesController.getCss(skin+".min.css")">
    }
    <link rel="shortcut icon" type="image/png" href="@routes.StaticFilesController.getImg("favicon.png")">
    <script async src="@routes.StaticFilesController.getJs("jquery-1.9.0.min.js")"></script>
    <script async src="@routes.StaticFilesController.getJs("bootstrap.min.js")"></script>
</head>
<body>
    <div class="container">
        @content
     </div>
    </body>
 </html>

введите здесь описание изображения


person user2054758    schedule 15.02.2013    source источник


Ответы (1)


Политика Never expire в этом контексте означает, что к вашему ответу следует добавить заголовок Expire, дата которого находится в далеком будущем, например, через 10 лет. Вы можете легко сделать это в Google Play, как описано в документе Manipulating the response. пример:

public static Result index() {
    response().setHeader(EXPIRES, "Thu, 16 Feb 2023 20:00:00 GMT");
    return ok("<h1>Hello World!</h1>");
}

Конечно, вместо того, чтобы указывать дату истечения срока действия в виде String, вы должны использовать какой-либо метод для ее динамического расчета, вы можете сделать это с помощью org.joda.time.DateTime (доступно в Play) и его методов, таких как: plusYears(int years). Самое главное, что это должно быть окончательно отформатировано в формате даты RFC 1123.

Редактировать, конечно, вы можете возвращать различные типы результатов, в том числе бинарные, как описано в doc (раздел "Результаты"), чтобы проверить все доступные параметры, загляните в API: play.mvc.Results это может быть: ok(Content content) - типично при рендеринге какого-то вида, ok(java.io.File content), ok(java.io.InputStream content) и т.д.

С другой стороны...

Я определенно рекомендую НЕ использовать Play для обслуживания far future static contents (а также любого другого статического общедоступного контента). Хотя вы можете сделать это легко, как описано выше, ИМХО, это избыточная трата ресурсов Play, поскольку вам нужно обрабатывать все запросы для любого отдельного изображения, сценария, стиля... чего угодно.

Рассмотрите возможность использования общего... HTTP-сервера для этой работы (nginx, lighthttpd, Apache) или, что еще лучше, какой-нибудь распределенный CDN. В этом случае ваше приложение может заботиться о выполнении своей логики вместо поиска таблиц стилей на диске.

PS Помните, что если вы используете Play вместо HTTP-сервера, для добавления нового статического содержимого, которое обслуживается из папки /public, вам потребуется перезапустить приложение, поэтому, по крайней мере, убедитесь, что вы сохраняете их в какой-то специальной папке отдельно от приложения, поэтому вы можете добавлять/удалять/заменять их, не останавливая приложение.

person biesior    schedule 15.02.2013
comment
спасибо, но как я могу вернуть двоичное содержимое изображения из контроллера, как вы сделали? - person user2054758; 16.02.2013
comment
спасибо решил! я буду продолжать использовать воспроизведение для статического ресурса, потому что теперь клиенты запрашивают содержимое статики только один раз (при первом запросе) - person user2054758; 16.02.2013
comment
В любом случае, я все еще рекомендую внешний сервер, проверьте мой пост scriptum :) - person biesior; 16.02.2013
comment
да, конечно, это лучшее решение, но в любом случае я хочу предложить простой способ запуска моего проекта с открытым исходным кодом на одном сервере. знаете ли вы, что актив/кофе и меньше ресурсов все еще компилируются без вызова класса активов? - person user2054758; 16.02.2013