静态资产从play framework 2.3.x中的绝对路径提供服务 [英] static asset serving from absolute path in play framework 2.3.x

查看:159
本文介绍了静态资产从play framework 2.3.x中的绝对路径提供服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要从不在类路径上的绝对路径提供图像文件。当我使用 Assets.at(路径,文件)时,它只在 / assets 内搜索。我已将URL映射到控制器函数,如下所示:

I need to serve image files from an absolute path that is not on the classpath. When I use Assets.at(path, file), it only searches inside /assets. I have mapped the url onto a controller function like the following:

public static Action<AnyContent> getImage(String imageId) {
    String path = PICTURE_UPLOAD_DIR; // here this path is absolute
    String file = imageId + ".png";
    return Assets.at(path, file);
}

我该如何运作?

注意:使用资产提供图像的原因是因为自动电子标记功能可以轻松发送未修改的http 304。似乎没有自动触发功能可以独立于资产提供

NOTE: The reason to make images served using Assets is because of the auto etagging feature that make easy to send http 304 not modified. It seems that there is no auto etagging feature that play provides independently from Assets

推荐答案

Assets.at()仅适用于在构建时添加到类路径的资产。
请参阅: https://www.playframework.com/documentation/2.4.x /资产

Assets.at() works only for assets added to the classpath at build-time. See: https://www.playframework.com/documentation/2.4.x/Assets

解决方案是从磁盘读取文件作为byte []然后返回响应正文中的byte []。

The solution would be to read the files from the disk as byte[] then return the byte[] in the response body.

将图像转换为byte [] (此解决方案仅适用于小文件,适用于大型文件查看流):

Converting the image to byte[] (this solution is for small files only, for large files look into streams):

private static Promise<byte[]> toBytes(final File file) {
    return Promise.promise(new Function0<byte[]>() {
        @Override
        public byte[] apply() throws Throwable {
            byte[] buffer = new byte[1024];
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            FileInputStream is = new FileInputStream(file);

            for (int readNum; (readNum = is.read(buffer)) != -1;) {
                os.write(buffer, 0, readNum);
            }
            return os.toByteArray();
        }
    });
}

使用toBytes()为图像提供服务的控制器: / strong>

The controller that uses toBytes() to serve the image:

public static Promise<Result> img() {
    //path is sent as JSON in request body
    JsonNode path = request().body().asJson();

    Logger.debug("path -> " + path.get("path").asText());
    Path p = Paths.get(path.get("path").asText());
    File file = new File(path.get("path").asText());

    try {
        response().setHeader("Content-Type", Files.probeContentType(p));
    } catch (IOException e) {
        Logger.error("BUMMER!", e);
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                return badRequest();
            }
        });
    }

    return toBytes(file).map(new Function<byte[], Result>() {
        @Override
        public Result apply(byte[] bytes) throws Throwable {
            return ok(bytes);
        }       
    }).recover(new Function<Throwable, Result>() {
        @Override
        public Result apply(Throwable t) throws Throwable {
            return badRequest(t.getMessage());
        }
    });
}

路线:

POST    /img    controllers.YourControllerName.img()


(不添加如果使用ETag标头,则不需要日期或最后修改的标头):

(not adding Date or Last-Modified headers as they are not needed if ETag header is used instead):

获取文件的SHA1:

private static Promise<String> toSHA1(final byte[] bytes) {       
    return Promise.promise(new Function0<String>() {
        @Override
        public String apply() throws Throwable {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            byte[] digestResult = digest.digest(bytes);
            String hexResult = "";
            for (int i = 0; i < digestResult.length; i++) {
                hexResult += Integer.toString(( bytes[i] & 0xff ) + 0x100, 16).substring(1);
            }
            return hexResult;
        }
    });
}

设置ETag标题:

private static boolean setETagHeaders(String etag, String mime) {
    response().setHeader("Cache-Control", "no-cache");
    response().setHeader("ETag", "\"" + etag + "\"");
    boolean ifNoneMatch = false;

    if (request().hasHeader(IF_NONE_MATCH)) {
        String header = request().getHeader(IF_NONE_MATCH);
        //removing ""
        if (!etag.equals(header.substring(1, header.length() - 1))) {
            response().setHeader(CONTENT_TYPE, mime);
        } 
        ifNoneMatch = true;
    } else {
        response().setHeader(CONTENT_TYPE, mime);
    }
    return ifNoneMatch;
}

支持ETag的控制器:

public static Promise<Result> img() {
    //path is sent as JSON in request body
    JsonNode path = request().body().asJson();
    Logger.debug("path -> " + path.get("path").asText());
    Path p = Paths.get(path.get("path").asText());
    File file = new File(path.get("path").asText());        
    final String mime;

    try {
        mime = Files.probeContentType(p);            
    } catch (IOException e) {
        Logger.error("BUMMER!", e);
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                return badRequest();
            }
        });
    }
    return toBytes(file).flatMap(new Function<byte[], Promise<Result>>() {
        @Override
        public Promise<Result> apply(final byte[] bytes) throws Throwable {
            return toSHA1(bytes).map(new Function<String, Result>() {
                @Override
                public Result apply(String sha1) throws Throwable {
                    if (setETagHeaders(sha1, mime)) {
                        return status(304);
                    }
                    return ok(bytes);
                }
            });
        }
    }).recover(new Function<Throwable, Result>() {
        @Override
        public Result apply(Throwable t) throws Throwable {
            return badRequest(t.getMessage());
        }
    });
}



$
一些缺点(总是有一个但是):


  1. 这是阻塞。因此最好在另一个配置为阻止IO的Akka线程池上执行它。

  2. 如上所述,转换为byte []仅适用于小文件,因为它使用内存进行缓冲。在您只提供小文件(想想网站级图像)的情况下,这不应该是一个问题。请参阅: http://docs.oracle.com/javase/tutorial/essential /io/file.html 了解使用NIO2读取文件的不同方法。

  1. This is blocking. So it's better to execute it on another Akka thread-pool configured for blocking IO.
  2. As mentioned, the conversion to byte[] is for small files only, as it uses the memory for buffering. This should not be a problem in the case where you only serve small files(think web site grade images). See: http://docs.oracle.com/javase/tutorial/essential/io/file.html for different ways to read files using NIO2.

这篇关于静态资产从play framework 2.3.x中的绝对路径提供服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆