使用java的 htpUrlConnection post请求 下载pdf文件,然后输出到页面进行预览和下载

因为pdf文件存在第三方系统,只能通过接口调用去获取文件。第三方接口返回的是response.getOutPutStream.write(byte[])的响应流。我所涉及的代码逻辑就是:

    无论是页面预览pdf的请求还是下载文件的请求,都访问本方法。接收参数,然后post请求第三方接口下载文件得到流【httpurlconnection的post请求的使用】,然后转byte数组【这一步很关键,参考的网页已经找不到了,但是非常感谢原文章作者】,然后同样用响应流输出到页面【out.write()】

代码如下【至于后续代码的规范划的拆分、优化暂时就不粘贴在这里了】:

/**
	 * "MY_LICENCE_DETAIL"
	 * @author      作        者: 
	 * @createdTime 创建时间:2018年5月16日 下午4:17:27
	 * @description 方法说明:根据    infoFileId  文件绝对路径   查询   文件   页面展示并提供下载
	 * @param fileId  文件路径
	 * @param  fileName 文件名字
	 * @param  flag 预览还是下载标志位         flag 为null默认预览 , flag不为null为下载
	 * @throws UnsupportedEncodingException 
	 */
	@RequestMapping(value = "/common/xxxxx.do", method = RequestMethod.GET)
	public void xxxxxttttt(String fileId,String fileName,String flag, HttpServletResponse response) throws IOException{
		log.info("------接受到     入参fileId是"+fileId+"---fileName是"+fileName+"---flag是"+flag+"---");
		fileId= new String(fileId.getBytes("iso-8859-1"),"utf-8");
		fileName= new String(fileName.getBytes("iso-8859-1"),"utf-8");
		log.info("------转码处理后     入参fileId是"+fileId+"---fileName是"+fileName+"---flag是"+flag+"---");
		if(null == fileName || "".equals(fileName)){
			fileName = "证件文件.pdf";
		}else{
			//请求的接口的文件均为pdf格式
			fileName = fileName+".pdf";
		}
		if(fileId == null || "".equals(fileId)){
			log.info("参数无效");
			ResultUtil.sendString(response, "参数无效");
		}else{       
			//直接请求接口下载文件
			InputStream in = null;
			OutputStream out = null;
			try{ 
			    //相关接口url配置在xml读取
			    String stringUrl = GlobalConfig.getInstance().getString("my.xxx.detail.xxxxx.xxxxxxxx");
			    log.info("-----请求的url地址是:"+stringUrl+"----");
			    URL url = new URL(stringUrl);
			    URLConnection urlConnection = url.openConnection();
			    HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
			    httpURLConnection.setDoOutput(true);			
			    httpURLConnection.setDoInput(true);				
			    httpURLConnection.setUseCaches(false); 			
			    httpURLConnection.setRequestMethod("POST");		   
			    httpURLConnection.setRequestProperty("Charsert", "UTF-8");
			    httpURLConnection.setRequestProperty("connection", "Keep-Alive");
			    httpURLConnection.setConnectTimeout(60000);
			    httpURLConnection.setReadTimeout(60000);
			    httpURLConnection.setInstanceFollowRedirects(true);
			    httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			   
			    httpURLConnection.connect();
			    // 创建输入输出流,用于往连接里面输出携带的参数,(输出内容为?后面的内容)  正文,正文内容其实跟get的URL中 '? '后的参数字符串一致  
			    DataOutputStream dataout = new DataOutputStream(httpURLConnection.getOutputStream());
			    String parm = "fileId=" + URLEncoder.encode(fileId, "utf-8");  
			    // 为字符串进行编码            将参数输出到连接
			    dataout.writeBytes(parm);
			    // 输出完成后刷新并关闭流
			    dataout.flush();
			    dataout.close(); // 重要且易忽略步骤 (关闭流,切记!) 
			    
			    in = httpURLConnection.getInputStream();//这一步才是真正的发送请求并获取相应流结果
			    //把inputstream转为byte数组
			    ByteArrayOutputStream outTemp=new ByteArrayOutputStream();
		            byte[] buffer=new byte[1024*4];
		            int n=0;
		            while ( (n=in.read(buffer)) !=-1) {
		        	outTemp.write(buffer,0,n);
		            }
		            byte[] fileData =  outTemp.toByteArray();
			    
		            //获取文件大小
			    int contentLength = httpURLConnection.getContentLength();  
			    log.info("-----获取文件长度httpURLConnection.getContentLength() = "+contentLength+"-----");
			    int responceCode = httpURLConnection.getResponseCode();
			    log.info("----responceCode==="+responceCode+"----");
			    if (responceCode == HttpURLConnection.HTTP_OK){
		            response.reset();
		            // 设置response的Header,  对文件进行url编码
			        String name = new String(URLEncoder.encode(fileName, "UTF-8").getBytes("UTF-8"),"ISO8859-1");
			        //预览还是下载,设置不同的响应头
			        if(flag == null){
			        	response.setHeader("content-disposition", "inline;filename=" + name);
			        	log.info("---预览----");
			        }else{
			        	response.setHeader("content-disposition", "attachment;filename=" + name);
			        	log.info("---下载----");
			        }
			        response.setContentLength(fileData.length);
			        out = response.getOutputStream();
			        out.write(fileData);
			        out.flush();
			     }else{
			    	 log.info("-----从接口下载文件 失败,responceCode != 200-----");
				 ResultUtil.sendString(response, "获取文件失败");
			     }
			}catch(Exception e){ 
				log.info("-----获取文件异常--------");
			    e.printStackTrace();
			    ResultUtil.sendString(response, "获取文件异常");
			}finally{ 
				 
			    try {
			        if(in != null){
			            in.close();
			        }
			    } catch (IOException e) {
			        e.printStackTrace();
			    }
			    try {
			    	if(out != null){
			    		out.close();
			    	}
			    } catch (IOException e) {
			    	e.printStackTrace();
			    }
			    
			}
			
		}

预览和下载都是访问本方法,只是下载比预览多传递一个flag参数。根据falg以此来设置响应头content-disposition",是attachment【下载】还是在页面  inline【预览】

页面上的预览代码为页面引入的js中自动初始化加载一个方法,在这个方法中把预览的请求url赋值给一个iframe的src属性:

 js代码:
//dom元素
        var $iframe=$('#iframe-viewpdf');

        var xxxx= {
    		init: function() {
    			var url = CONTEXTPATH + '/common/xxxxxx.do?fileId=' + fileId +'&fileName=' + fileName;
            	$iframe.attr('src',url); 
            }
        };
jsp代码:
<iframe id="iframe-viewpdf"  src="" frameborder="0" width="100%" height="100%"></iframe>  

整体上功能就实现了。点击查看在页面预览pdf文件,点击下载,文件直接下载到本地。

但是这一路上走了很多弯路,有个人原因,也有客观原因【很蛋疼,涉及到系统对接、各种扯皮、心累】。甚是折腾。

我把一路上遇到的问题回忆下,把有用的信息记录在此:

①本来请求第三方接口下载文件,是不能按我上面代码的方式直接调用的,因为公司有一个专门的转发系统【gsb】,我要组织参数发给gsb,在gsb里配置第三方接口的信息,然后gsb自动去请求,然后把结果自动转给我,我只负责和gsb交互就行。但是之前都是gsb接收返回json字符串的配置,现在那边给gsb返回的是文件流,报错。问了一大帮人都没遇到过这种情况,尝试各种参数配置组合都不行,卡着了。没办法为了先实现功能,我就另辟蹊径选择了用java请求文件,先实现功能。

【这是后话了,直到昨天才确定,调的接口那边,要把文件byte[],不要直接放在out.write()里面返回给gsb,要把byte[]转为base64字符串再返回,才能被gsb系统识别接收。。。。。。是他那边返回方式的问题。说到这了,那就把这个内容  byte[] 与base64字符串的  编码解码   记录在这。】

byte[] fileData = preFormFileServiceImpl.downloadByPath(fileId);

String file = Base64.encodeToString(fileData);    //注意,别导错包了   import jodd.util.Base64

接收String以后要转回byte数组。代码如下:

jodd.util.Base64 decoder = new jodd.util.Base64();
// Base64解码  接受的gsb结果转byte 数组

byte[] fileData = decoder.decode(resultStr);

if (fileData == null) {
	log.info("-----获取文件gsb返回结果转byte[]为null--------");
        ResultUtil.sendString(response, "");
}else{
        for (int i = 0; i < fileData.length; ++i) {
		if (fileData[i] < 0) {// 调整异常数据
			fileData[i] += 256;
		}

	}

        //接下来设置response的响应头等信息......

②关于使用httpclient发送请求。有几种方式,我选择的是使用httpUrlConnection发送post请求。可是页面上啥也没有。排查发现

httpURLConnection.getContentLength()获取返回的文件大小  输出  是 0,

然而返回的状态码httpURLConnection.getResponseCode()  输出是 200,纳闷了啊。成功但是没有下载到文件。

好一通折腾,比如之前我是这样添加请求参数的,

httpURLConnection.setRequestProperty(”key“,"value");

后来发现要先用流把参数放进去,这样:

 // 创建输入输出流,用于往连接里面输出携带的参数,(输出内容为?后面的内容)  正文,正文内容其实跟get的URL中 '? '后的参数字符串一致  
			    DataOutputStream dataout = new DataOutputStream(httpURLConnection.getOutputStream());
			    String parm = "fileId=" + URLEncoder.encode(fileId, "utf-8");  
			    // 为字符串进行编码
			    // 将参数输出到连接
			    dataout.writeBytes(parm);
			    // 输出完成后刷新并关闭流
			    dataout.flush();
			    dataout.close(); // 重要且易忽略步骤 (关闭流,切记!) 

之后再去发送请求。

还有关于请求成功,但是流为0 ,有不少博客说加上这一行:

//解决  下载文件大小httpURLConnection.getContentLength() 为0 的问题

//httpURLConnection.setRequestProperty("Accept-Encoding", "identity");

也没有效果。还是为0。

总之各种尝试,相关的博客基本上翻遍了。。。现在我怀疑是接口那边文件的问题,换个测试参数值,好了,不为  0 了。吐血。。。。至于上面提到的两种post请求参数的设置方式,我目前用的第二种,第一种没有再尝试,但我认为应该也是可以的,因为在别人博客中使用过。

③接下来就是输出数据到页面了。httpUrlConnection 发送请求返回的接收是一个InPutStream in.

in = httpURLConnection.getInputStream();  //这一步才是真正发送请求并接收返回结果。

之前一直是这样写的返回代码

bin = new BufferedInputStream(in);//把InPutStream 转为 BufferedInputStream
			        out = response.getOutputStream();
			        int size = 0;
			        //读取文件流  
			        byte[] buf = new byte[1024];
				    if(bin.read(buf) == -1){
				    	log.info("=====读取下载的文件流,出现【bin.read(buf) == -1】的情况====");
						ResultUtil.sendString(response, "文件不存在");
			        } 
			        while((size = bin.read(buf)) != -1){
			        	//写文件流 
			        	out.write(buf,0,size);
			        }
			        bin.close();
			        out.flush();

但是页面一直啥也没有,感觉也没啥问题啊,其他博客还有以前记得就是这么写的。。。迷惑【流的知识点我一直很薄弱,理解不透还经常记混淆,有大神看出问题还望指正交流】

后来,把返回的iuputStream流转为byte数组,直接全部out.write(byte[]),也别搞什么while循环了。问题解决【代码在开头,此处就不再粘贴了】。关一这一点,不是很明白。

④还漏了一点,就是关于pdf文件在页面预览,这个问题也折腾。目前我用的iframe src的方式。后端response的header设置上,有说设置response.setContentType("application/pdf");的,可我最终没有这一步也成功了,总之各种说法都有,都看得乱了。一点点摸索尝试。对了,关于ajax是不能  直接  下载文件的,因为传输数据形式在限制。但是有巧妙办法,相关办法很多博客有说,可以自行查阅。

下面的代码是自己尝试的最后代码:没有成功。有兴趣可以对比下差异【流转byte[]、直接byte[]全部out.write()】

@RequestMapping(value = "/common/xxxxxx.do", method = RequestMethod.GET)
	public void xxxxxxx(String fileId,String fileName,String flag, HttpServletResponse response) throws UnsupportedEncodingException{
		 
		fileId= new String(fileId.getBytes("iso-8859-1"),"utf-8");
		fileName= new String(fileName.getBytes("iso-8859-1"),"utf-8");
		
		log.info("------入参fileId是"+fileId+"---fileName是"+fileName+"---flag是"+flag+"---");
		if(null == fileName || "".equals(fileName)){
			fileName = "文件.pdf";
		}else{
			fileName = fileName+".pdf";
		}
		if(fileId == null || "".equals(fileId)){
			log.info("传入参数无效");
			ResultUtil.sendString(response, "");
		}else{
			//直接wen  jian  接口
			InputStream in = null;
			OutputStream out = null;
			BufferedInputStream bin = null;
			try{
				 
				//接口url配置在xml读取
				String stringUrl = GlobalConfig.getInstance().getString("my.xxx.detail.xxx.downloadPdfByCode");
				log.info("-----请求的url地址是:"+stringUrl+"----");
			    URL url = new URL(stringUrl);
			    URLConnection urlConnection = url.openConnection();
			    HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
			    
			    httpURLConnection.setDoOutput(true);			
			    httpURLConnection.setDoInput(true);				
			    httpURLConnection.setUseCaches(false); 			
			    httpURLConnection.setRequestMethod("POST");		   
			    httpURLConnection.setRequestProperty("Charsert", "UTF-8");
			    httpURLConnection.setRequestProperty("connection", "Keep-Alive");
			    httpURLConnection.setConnectTimeout(60000);
			    httpURLConnection.setReadTimeout(60000);
			    httpURLConnection.setInstanceFollowRedirects(true);
			    httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			   
			    //解决  下载文件大小httpURLConnection.getContentLength() 为0 的问题
			    //httpURLConnection.setRequestProperty("Accept-Encoding", "identity");
			    
			    httpURLConnection.connect();
			    
			    // 创建输入输出流,用于往连接里面输出携带的参数,(输出内容为?后面的内容)  正文,正文内容其实跟get的URL中 '? '后的参数字符串一致  
			    DataOutputStream dataout = new DataOutputStream(httpURLConnection.getOutputStream());
			    String parm = "fileId=" + URLEncoder.encode(fileId, "utf-8");  
			    // 为字符串进行编码
			    // 将参数输出到连接
			    dataout.writeBytes(parm);
			    // 输出完成后刷新并关闭流
			    dataout.flush();
			    dataout.close(); // 重要且易忽略步骤 (关闭流,切记!) 
			    
			    in = httpURLConnection.getInputStream();
			    //获取文件大小
			    int contentLength = httpURLConnection.getContentLength();  
			    log.info("-----获取文件长度httpURLConnection.getContentLength() = "+contentLength+"-----");
			    int responceCode = httpURLConnection.getResponseCode();
			    log.info("----responceCode==="+responceCode+"----");
			    if (responceCode == HttpURLConnection.HTTP_OK){
				    // 清空response
		            response.reset();
		            response.setCharacterEncoding("utf-8");
		            response.setContentType("application/pdf");
		            // 设置response的Header,  对文件进行url编码
			        String name = URLEncoder.encode(fileName, "UTF-8");
			        
			        //预览还是下载
			        if(flag == null){
			        	response.setHeader("Content-Disposition", "inline;filename="+name);
			        	log.info("---预览----");
			        }else{
			        	response.setHeader("Content-Disposition", "attachment;filename="+name);
			        	log.info("---下载----");
			        }
			        //response.setHeader("Content-Length",""+contentLength);
			        
			        bin = new BufferedInputStream(in);
			        out = response.getOutputStream();
			        int size = 0;
			        //读取文件流  
			        byte[] buf = new byte[1024];
				    if(bin.read(buf) == -1){
				    	log.info("=====读取下载的文件流,出现【bin.read(buf) == -1】的情况====");
						ResultUtil.sendString(response, "文件不存在");
			        } 
			        while((size = bin.read(buf)) != -1){
			        	//写文件流 
			        	out.write(buf,0,size);
			        }
			        bin.close();
			        out.flush();
			     }else{
			    	 log.info("-----从接口下载文件 失败,responceCode != 200-----");
					 ResultUtil.sendString(response, "获取文件失败");
			     }
			}catch(Exception e){ 
				log.info("-----获取文件异常--------");
			    e.printStackTrace();
			    ResultUtil.sendString(response, "获取文件异常");
			}finally{ 
				
			    try {
			        if(in != null){
			            in.close();
			        }
			    } catch (IOException e) {
			        e.printStackTrace();
			    }
			    try {
			    	if(out != null){
			    		out.close();
			    	}
			    } catch (IOException e) {
			    	e.printStackTrace();
			    }
			    
			}
		}
	}



猜你喜欢

转载自blog.csdn.net/xiaoanzi123/article/details/80596524
今日推荐