Grails を介してファイルをダウンロードしており、このファイルがこのユーザーによってダウンロードされたという事実を、次のようなコードで記録しています。
class MyController {
private void recordTrackDownload() {
def d = new Download(session.user, "/path/to/file")
d.save()
}
def download = {
def f = new File("/path/to/file")
recordTrackDownload()
response.contentType = "mime/type"
response.outputStream << f.newInputStream()
response.outputStream.flush()
}
}
また、クリックごとにダウンロードの記録がいくつか表示されます。これは、Firefox によって記録されたいくつかのリクエストと、ライブ HTTP ヘッダーに表示される「Transfer-Encoding: chunked」ヘッダーと関係があると思われます。(この動作は Chromium でも見られます)
また、ターミナルにエラーが記録されていることもわかります。これはおそらく関連しており、Grails のバグであるか、ログに記録されて無視されたバグである可能性がありますが、ファイルの実際のダウンロードは停止していないようです。
2010-01-14 18:46:16,623 [http-8080-4] ERROR errors.GrailsExceptionResolver - Executing action [download] of controller [com.tunited.music.DownloadTrackController] caused exception: ClientAbortException: java.net.SocketException: Connection reset
org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Executing action [download] of controller [com.tunited.music.DownloadTrackController] caused exception: ClientAbortException: j
ava.net.SocketException: Connection reset
at java.lang.Thread.run(Thread.java:636)
Caused by: org.codehaus.groovy.runtime.InvokerInvocationException: ClientAbortException: java.net.SocketException: Connection reset
... 1 more
Caused by: ClientAbortException: java.net.SocketException: Connection reset
at com.tunited.music.DownloadTrackController$_closure2.doCall(DownloadTrackController.groovy:35)
at com.tunited.music.DownloadTrackController$_closure2.doCall(DownloadTrackController.groovy)
... 1 more
Caused by: java.net.SocketException: Connection reset
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
... 3 more
2010-01-14 18:46:17,091 [http-8080-4] ERROR view.GroovyPageView - Error processing GroovyPageView: getOutputStream() has already been called for this response
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at home_ed_dev_nomad_tunited_grails_app_views_error_gsp.run(home_ed_dev_nomad_tunited_grails_app_views_error_gsp:19)
at java.lang.Thread.run(Thread.java:636)
2010-01-14 18:46:17,092 [http-8080-4] ERROR errors.GrailsExceptionResolver - getOutputStream() has already been called for this response
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at java.lang.Thread.run(Thread.java:636)
2010-01-14 18:46:17,100 [http-8080-4] ERROR view.GroovyPageView - Error processing GroovyPageView: getOutputStream() has already been called for this response
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at home_ed_dev_nomad_tunited_grails_app_views_error_gsp.run(home_ed_dev_nomad_tunited_grails_app_views_error_gsp:19)
at java.lang.Thread.run(Thread.java:636)
2010-01-14 18:46:17,103 [http-8080-4] ERROR [/].[grails] - Servlet.service() for servlet grails threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at org.codehaus.groovy.grails.web.sitemesh.GrailsPageResponseWrapper$5.activateDestination(GrailsPageResponseWrapper.java:141)
at org.codehaus.groovy.grails.web.sitemesh.GrailsRoutablePrintWriter.getDestination(GrailsRoutablePrintWriter.java:41)
at org.codehaus.groovy.grails.web.sitemesh.GrailsRoutablePrintWriter.flush(GrailsRoutablePrintWriter.java:159)
at org.codehaus.groovy.grails.web.util.GrailsPrintWriter.flush(GrailsPrintWriter.java:98)
...
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Thread.java:636)
2010-01-14 18:46:17,106 [http-8080-4] ERROR [/].[default] - Servlet.service() for servlet default threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at org.codehaus.groovy.grails.web.sitemesh.GrailsPageResponseWrapper$5.activateDestination(GrailsPageResponseWrapper.java:141)
at org.codehaus.groovy.grails.web.sitemesh.GrailsRoutablePrintWriter.getDestination(GrailsRoutablePrintWriter.java:41)
at org.codehaus.groovy.grails.web.sitemesh.GrailsRoutablePrintWriter.flush(GrailsRoutablePrintWriter.java:159)
at org.codehaus.groovy.grails.web.util.GrailsPrintWriter.flush(GrailsPrintWriter.java:98)
...
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Thread.java:636)
理想的には、コントローラー コードを 1 回だけ実行し、チャンク ファイルをダウンロードしたいと考えています。最初のアクションでダウンロードを記録し、次にファイルをダウンロードする 2 番目のアクションにリダイレクトします。しかし、途中で別のリダイレクトを配置し、フラッシュ スコープなどを使用して 2 番目の URL を保護する必要があるのは、不十分なソリューションのようです。HTTP プロトコルまたは Grails がどのように機能するかについて私が知らないことがあるようですが、これを修正する簡単な方法があります。
何か案は ?