1 问题
Android app里面写了一个Java socket的简单服务器,在浏览器里面输入相应的IP和端口访问服务器下载文件,Java socket怎么写返回数据的头部信息,浏览器才知道需要下载文件的名字呢?
2 关于Content-Disposition
在常规的HTTP应答中,Content-Disposition
响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。
Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
1)格式说明:
content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm )
2)字段说明:Content-Disposition为属性名disposition-type是以什么方式下载,如attachment为以附件方式下载disposition-parm为默认保存时的文件名服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment:
3) 备注:这样浏览器会提示保存还是打开,即使选择打开,也会使用相关联的程序比如记事本打开,而不是IE直接打开了。Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
复制代码 代码如下:
@RequestMapping("/downloadTemplate")
public byte[] downloadTemplate(HttpServletRequest request,HttpServletResponse response){
String fileName = "Budget_Template.xlsx";
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
OutputStream out;
try {
out = response.getOutputStream();
URL base = this.getClass().getResource(""); //先获得本类的所在位置,如/home/popeye/testjava/build/classes/net/
String path = new File(base.getFile(), "/template.xlsx").getCanonicalPath();
byte[] buf = new byte[1024];
InputStream in = new FileInputStream(new File(path));
int tempbyte;
while ((tempbyte = in.read(buf)) != -1) {
out.write(buf);
}
out.flush();
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
很明显,这个代码是在Java 作为服务端spring mvc里面的使用,但是我们现在是直接Java socket来写服务端代码,那我们怎么封装呢,直接代码如下
"Content-Disposition: attachment; filename=" + fileName+ "\r\n"
3 解决
String line="HTTP/1.1 200 OK \r\n";
Log.i(TAG,"line="+line);
//sout.writeChars(line);
sout.write(line.getBytes());//用字节传输,不能用字符,浏览器无法解析
// String header="Content-Type: application/vnd.android.package-archive; charset=utf-8 \r\n"
String header="Content-Type: application/octet-stream; charset=utf-8 \r\n"
+ "Content-Disposition: attachment; filename=" + fileName+ "\r\n"
+ "Content-length: "+file.length()+" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
这里的sout是通过socket得到的
socket.getOutputStream()
"Content-Disposition: attachment; filename=" + fileName+ "\r\n"
当然filename参数可以包含路径信息,但User-Agnet会忽略掉这些信息,只会把路径信息的最后一部分做为文件名。
当你在响应类型(Content-Type)为application/octet- stream情况下使用了这个头信息的话,那就意味着你不想直接显示内容,而是弹出一个"文件下载"的对话框,接下来就是由你来决定"打开"还是"保存" 了。
但是我这里如果是需要下载apk文件的话, 我们可以看下Content-Type对照表(https://tool.oschina.net/commons/)我直接设置Content-Type为application/vnd.android.package-archive 也可以
application/vnd.android.package-archive
String header="Content-Type: application/vnd.android.package-archive; charset=utf-8 \r\n"
// String header="Content-Type: application/octet-stream; charset=utf-8 \r\n"
+ "Content-Disposition: attachment; filename=" + fileName+ "\r\n"
+ "Content-length: "+file.length()+" \r\n\r\n";
Log.i(TAG,"header="+header);
sout.writeBytes(header);
参考链接:
https://www.jianshu.com/p/4c52cb691f54
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
https://tool.oschina.net/commons/
来源:oschina
链接:https://my.oschina.net/u/4264621/blog/3232710