Java-Servlet:Quicktime 视频流式传输导致 ClientAbortException
我正在尝试编写一个基于 java-servlet 的小型网络代理。该代理有 只有一项任务:
- HTML 应传递到发送请求的客户端(目标端口 80 的 GET 请求将通过防火墙规则重新路由到代理)。
- 嵌入的 Quicktime-Video 应被另一个 QuickTime-Video 替换,并应传递给客户端而不是最初请求的(这里,客户端最初发送的 GET 请求也首先被重新路由到代理。代理检测到请求的内容类型不是 HTML,并从此问题得出结论,在这种特殊情况下,内容必须是 Quicktime 视频(请参阅下面的 HTML)。因此,代理应向客户端发送/流式传输特定视频。
对于客户端将要请求的以下 HTML 文档,应该会发生这种情况:
<html>
<head>
<h1> Heading!</h1>
</head>
<body>
<h1>My First Heading</h1>
<object width="160" height="144"
classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
codebase="http://www.apple.com/qtactivex/qtplugin.cab">
<param name="src" value="final_lion.mov">
<param name="autoplay" value="true">
<param name="controller" value="false">
<embed src="final_lion.mov" width="480" height="432"
autoplay="true" controller="false"
pluginspage="http://www.apple.com/quicktime/download/">
</embed>
</object>
</body>
</html>
proxy-servlet 的源代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
PrintWriter out = null;
try {
String result = new String();
String urlStr = request.getRequestURL().toString();
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
response.setContentType(conn.getContentType());
if (conn.getContentType().contains("text/"))
{
out = response.getWriter();
out.write(getTextContent(conn));
out.flush();
out.close();
}
else
{
File file = new File("/var/www/final_lion.mov");
response.setContentType("video/quicktime");
response.setHeader("Content-Length", Long.toString(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
FileInputStream input = new FileInputStream(file);
OutputStream output = response.getOutputStream();
byte[] buffer = new byte[4096];
int read = 0;
while((read = input.read(buffer)) != -1 ){
output.write(buffer,0,read);
}
input.close();
output.flush();
output.close();
}
} catch (Exception e) {
context.log("error", e);
}
}
private String getTextContent(URLConnection conn)
{
try{
BufferedReader rd = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line + "\n");
}
rd.close();
return sb.toString();
}catch (Exception e){
return "error: " + e.toString();
}
}
HTML 文档由代理传递并由浏览器正确呈现。但是,quicktime-video 的 GET 请求以及代理的响应会在 servlet 源代码的第 34 行导致 ClientAbortException: java.net.SocketException: Broken pipeline。
我尝试了各种类型的OutputStream(BufferedOutputStream、DataOutputStream、PipedOutputStream、ServletOutputStream)。他们都没有工作...
有人看到我的错误吗?
此致, 芦赤
I am trying to program a small webproxy based on a java-servlet. This proxy has
only one task:
- HTML shall be passed to the client which sent the request (GET requests with destination port 80 are rerouted to the proxy by firewall rules).
- An embedded quicktime-video shall be replaced by another quicktime-video and shall be passed to the client instead of the initially requested (here, too, the initially sent GET request of the client is rerouted to the proxy first. The proxy detects that the requested content type is not HTML and concludes from this issue that the content must be a quicktime-video in this special case (see HTML below)). As a result the proxy shall send/stream a particular video to the client.
This should happen for the following HTML document that is going to be requested by the client:
<html>
<head>
<h1> Heading!</h1>
</head>
<body>
<h1>My First Heading</h1>
<object width="160" height="144"
classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
codebase="http://www.apple.com/qtactivex/qtplugin.cab">
<param name="src" value="final_lion.mov">
<param name="autoplay" value="true">
<param name="controller" value="false">
<embed src="final_lion.mov" width="480" height="432"
autoplay="true" controller="false"
pluginspage="http://www.apple.com/quicktime/download/">
</embed>
</object>
</body>
</html>
The source code of the proxy-servlet is as follows:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
PrintWriter out = null;
try {
String result = new String();
String urlStr = request.getRequestURL().toString();
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
response.setContentType(conn.getContentType());
if (conn.getContentType().contains("text/"))
{
out = response.getWriter();
out.write(getTextContent(conn));
out.flush();
out.close();
}
else
{
File file = new File("/var/www/final_lion.mov");
response.setContentType("video/quicktime");
response.setHeader("Content-Length", Long.toString(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
FileInputStream input = new FileInputStream(file);
OutputStream output = response.getOutputStream();
byte[] buffer = new byte[4096];
int read = 0;
while((read = input.read(buffer)) != -1 ){
output.write(buffer,0,read);
}
input.close();
output.flush();
output.close();
}
} catch (Exception e) {
context.log("error", e);
}
}
private String getTextContent(URLConnection conn)
{
try{
BufferedReader rd = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line + "\n");
}
rd.close();
return sb.toString();
}catch (Exception e){
return "error: " + e.toString();
}
}
The HTML document is delivered by the proxy and rendered by the browser correctly. But the GET request for the quicktime-video respectively the response of the proxy causes a ClientAbortException: java.net.SocketException: Broken pipe at line 34 of the servlet-sourcecode.
I tried various kinds of OutputStreams (BufferedOutputStream, DataOutputStream, PipedOutputStream, ServletOutputStream). None of them did work...
Does anybody see my mistake?
Yours faithfully,
Ashiaka
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
许多流媒体播放器希望服务器端支持
Range
请求。通常 servlet 容器(Tomcat、JBoss AS、Glassfish 等)自己的默认 servlet 已经支持这一点。因此,如果有一种方法只需将文件夹添加到服务器配置即可将电影文件夹发布到网络中,这样您就不需要自行开发的 servlet,那么我会采用这种方法。例如,在 Tomcat 中,您只需将文件移动到
Tomcat/webapps/movies
文件夹中,或者将以下行添加到其/conf/server.xml
中即可实现此目的:如果根据配置选择将
final_lion.mov
文件放入Tomcat/webapps/movies
或/var/www/movies
文件夹中,那么你应该能够通过以下方式访问它http://localhost:8080/movies/final_lion.mov 无需任何自行开发的 servlet。相反,Tomcat 自己的DefaultServlet
将用于流式传输静态内容。但如果没有办法,那么您需要重写 servlet 代码,使其支持
Range
请求(也称为下载恢复)。您可以在本文中找到具体的启动示例。Lot of players for streaming media expects the server side to support
Range
requests. Usually the servletcontainer's (Tomcat, JBoss AS, Glassfish, etc) own default servlet supports this already. So if there's a way to publish the movie folder into the web by just adding the folder to the server configuration, so that you don't need a homegrown servlet for this, then I'd go on this route.For example, in Tomcat you could achieve this by just moving the file into
Tomcat/webapps/movies
folder, or by adding the following line to its/conf/server.xml
:If you put the
final_lion.mov
file inTomcat/webapps/movies
or/var/www/movies
folder depending on the configuration choice, then you should be able to access it by http://localhost:8080/movies/final_lion.mov without the need for any homegrown servlet. Instead, Tomcat's ownDefaultServlet
will be used to stream the static content.But if there's no way, then you need to rewrite your servlet code in such way that it supports
Range
requests (also known as download resumes). You can find a concrete kickoff example in this article.我遇到了同样的问题,花了几天时间尝试一一挖掘。
这与您的浏览器缓存问题有关。您可以尝试使用此代码。这对我有用。
response.addHeader("Cache-Control", "no-transform, max-age=0");
I have same issue faced and spend few days and try to dig one by one.
This is related to your browser cache issue. You may try to use this code. It works for me.
response.addHeader("Cache-Control", "no-transform, max-age=0");