在 Google AppEngine 中读取 Java 资源文件

Posted

技术标签:

【中文标题】在 Google AppEngine 中读取 Java 资源文件【英文标题】:Reading Java resource files in Google AppEngine 【发布时间】:2015-07-20 00:25:59 【问题描述】:

我设置了一个 Google AppEngine Java 项目,该项目调用 BigQuery 来显示查询作业的结果。我使用的示例代码和指令可以在here 找到。该应用程序运行并从我的开发机器检索查询,但是当我上传到 AppEngine 以在 appspot.com 上进行测试时,它似乎无法使用加载 client_secrets.json 文件(由于 javascript URL 授权而导致 dev 和 appspot.com 的不同文件)以下sn-p:

  static GoogleClientSecrets getClientCredential() throws IOException 
if (clientSecrets == null) 

    clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
            new InputStreamReader(new FileInputStream(
                new File("WEB-INF/client_secrets.json"))));

  Preconditions.checkArgument(!clientSecrets.getDetails().getClientId().startsWith("Enter ")
      && !clientSecrets.getDetails().getClientSecret().startsWith("Enter "),
      "Enter Client ID and Secret from https://code.google.com/apis/console/?api=bigquery "
      + "into bigquery-appengine-sample/src/main/resources/client_secrets.json");

return clientSecrets;

这是我的 appengine-web.xml 资源文件定义:

  <resource-files>
    <include path="**" />
  </resource-files>

client_secrets.json 文件已正确打包在我的 war 文件中。我得到的错误是:

> /
Uncaught exception from servlet
java.io.FileNotFoundException: /base/data/home/apps/s~tactile-reason-849/1.385872137632330782/WEB-INF/client_secrets.json (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:171)
    at com.google.api.client.sample.bigquery.appengine.dashboard.ServiceUtils.getClientCredential(ServiceUtils.java:71)
    at com.google.api.client.sample.bigquery.appengine.dashboard.ServiceUtils.newFlow(ServiceUtils.java:103)
    at com.google.api.client.sample.bigquery.appengine.dashboard.MainServlet.initializeFlow(MainServlet.java:125)
    at com.google.api.client.extensions.servlet.auth.oauth2.AbstractAuthorizationCodeServlet.service(AbstractAuthorizationCodeServlet.java:124)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.JdbcmysqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:257)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:482)
    at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:437)
    at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:444)
    at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:230)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:308)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:300)
    at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:441)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:234)
    at java.lang.Thread.run(Thread.java:745)

我可以尝试使用 ServletContext.getResourceAsStream() 代替,但是在示例应用程序中连接到 BigQuery 的类不是 Servlet 类,因此这需要对代码进行一些重组才能正确实现,Google's instructions 似乎表明我的代码应该可以工作。任何帮助表示赞赏,谢谢!

【问题讨论】:

你在使用 Maven/Gradle 吗?如果是这样,请考虑将文件放在 src/main/resources 目录中并尝试像@NamshubWriter 显示的那样加载它 【参考方案1】:

资源文件不在文件系统上,所以new File("/WEB-INF/client_secrets.json") 普通不起作用。相反,您通常需要将其作为资源加载:

InputStream resourceStream = Thread.currentThread().getContextClassLoader()
    .getResourceAsStream("/WEB-INF/client_secrets.json");

由于某种原因,AppEngine 不允许您使用类加载器加载 WEB-INF 下的资源(您可以使用上述技术加载 jar 文件中的资源)。

您可以在App中使用File API访问WEB-INF中的路径,但路径需要是相对的。假设文件 client_secrets.json 位于 WEB-INF 文件夹中,这应该可以:

clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
    new InputStreamReader(new FileInputStream(
        new File("WEB-INF/client_secrets.json"))));

要使用File API 读取WEB-INF 下的资源,您还需要确保您的appengine-web.xml 文件包含您的WEB-INF 中的资源

<resource-files>
  <include path="/**.json" />
</resource-files>

见Configuring appengine-web.xml和Why can't I read from this file

【讨论】:

感谢@NamshubWriter 的快速回复。我应用了您建议的更改,现在我收到了一个新错误:来自 servlet java.io.FileNotFoundException 的未捕获异常:/base/data/home/apps/s~tactile-reason-849/1.385858979910614152/WEB-INF/client_secrets .json(没有这样的文件或目录) @ChrisLumpkin 您的 appengine-web.xml sn-p 暗示此文件位于“类”下,但您的 Java 代码 sn-p 正试图在 WEB-INF 下直接访问它。这个资源在哪里?它是否来自您的一个 jar 文件?你能在你的 WAR 文件中查看它的确切位置吗? 我刚刚更新了上面的 sn-ps 以反映我所做的更改。战争文件确实有 WEB-INF/client_secrets.json。 @ChrisLumpkin &lt;resource-files&gt; 下的路径需要以斜杠开头。我更新了我的答案。 我更新了上面的代码和stacktrace;即使通配符资源包含在 appengine-web.xml 和您发送的 sn-p 中,我仍然收到 FileNotFoundException。如果您希望我提供更多信息,请告诉我。【参考方案2】:

作为 AppEngine 错误的解决方法,我创建了一个对象,其中包含我从 FirebaseCredentialsHelper 类构建的 Firebase 身份验证数据。返回的对象使用从 Firebase 控制台生成的字符串值。

然后我将 FirebaseCredentialsHelper 类返回的身份验证对象转换为使用 Gson library 的 json,然后转换为字节输入流。

FirebaseApp.initializeApp(FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(Gson().toJson(FirebaseCredentialsHelper.get()).byteInputStream()))
.build()) 

注意:我将 Firebase 身份验证对象排除在版本控制之外,因为它们包含私有信息。

【讨论】:

以上是关于在 Google AppEngine 中读取 Java 资源文件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 App Engine 中从 Google Storage 读取 zip 文件?

使用 Google AppEngine 创建 Java Web 服务

在 Google Appengine 中部署 MobileFirst 混合应用程序

是否可以在 Google appengine 标准环境中使用 Gorilla mux 路由器?

在google appengine上部署ruby + sinatra应用程序

可以将Google Appengine的管理控制台集成到spring-boot项目中吗?