新人小白一枚,记录下工作中遇到的一点问题和解决方案。仅供自己以后复习参考之用。欢迎大神指导,交流。
刚开始学写博客,不好的地方请委婉的指出来哦。
先来说说需求背景
使用java来实现从网站网页获取内容(抓取网页)。可能会遇到以下两种情况(我暂时就只是遇到了这两种情况)。
1、java后台程序可以通过外网直接下载
这个就不做过多的论述,直接原样抓取过来,后台通过java中的DOM操作,批量的将a标签中的href属性和img标签中src属性修改为绝对路径即可,直接下载或者访问。
2、java后台程序不能通过外网直接下载
这里有存在两种情况:
第一种:通过浏览器直接访问网站,可以正常下载,但是通过java后台代码跳转访问却不能下载附件/图片。出现这种情况的原因是网站对不能下载的相关内容添加了防盗链。
第二种:通过浏览器不能直接访问,需要通过VPN / 服务器代理 等方式,才能访问的内网资源网站。
以上两种情况就是本文讨论的重点,可以通过接下来即将介绍的一种方式统一解决。
实际业务需求
一般的访问形式是这样的,存在两台服务器,服务器A是某种组织机构的,是内网才能访问,服务器B另一家组织机构的是外网可以直接访问的。现在两家要要合作,需要实现的是:用户可以通过服务器B访问服务器A中指定的网站(该网站具有登录验证功能)。
注:B能访问到A,需要A将B的网络地址添加到白名单。
当用户通过B登录A的网站时,需要我们在后台记录下cookie、header等一系列验证参数,并将它们添加到消息头发送给A的服务器,验证通过即可正常访问该网站。但是a标签中hre链接和img中src连接,却不能直接传入相关参数,这就会导致访问失败。不能显示图片和下载相关附件。
解决:将不能直接获取到的内容,先下载到服务器B,再将不能获取到内容的相关链接替换为下载到B上资源的链接。当用户需要下载时,就是直接从B上获取资源下载。
功能实现代码
/** * 保存附件到本地并返回本地文件名 * @param fileUrl 附件网络地址 * @param suffix 附件后缀名 * @return 本地附件名称 */ public String getLocalFileUrl(String fileUrl, String suffix ){ String localFileUrl = null; String fileName = null; try { Md5Util md5 = Md5Util.getInstance(); fileName = md5.string2string(fileUrl)+ "." +suffix; URL url = new URL(fileUrl); //设置代理 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("内网网络地址", 端口号)); //打开链接 HttpURLConnection conn = (HttpURLConnection)url.openConnection(proxy); //添加header验证信息 conn.setRequestProperty("Proxy-Authorization", "用户名:密码"); conn.setRequestProperty("Host", "网址"); conn.setRequestProperty("Cookie", "XXXXXXXXX"); //设置请求方式为"GET" conn.setRequestMethod("GET"); //超时响应时间为5秒 conn.setConnectTimeout(5 * 1000); //通过输入流获取数据 InputStream inStream = conn.getInputStream(); //得到二进制数据,以二进制封装得到数据,具有通用性 byte[] data = readInputStream(inStream); //new一个文件对象用来保存,默认保存当前工程根目录 HttpServletRequest request = ServletActionContext.getRequest(); //获取服务器缓存目录 String directoryURL = request.getSession().getServletContext().getRealPath("/"); //D:\workspace\.metadata\.me_tcat7\webapps\EducationalSystem\ // String directoryURL = "/home/........"; //线上环境linuxs此处我是用的是绝对路径 //创建本地文件保存目录 File fileMkdir = new File(directoryURL + "/cacheFile/"+supportSchoolId()); if(!fileMkdir.exists()){ fileMkdir.mkdirs(); } localFileUrl = fileMkdir + "/" + fileName; //文件路径 File localFile = new File(localFileUrl); if(!localFile.exists()){ //创建文件 localFile.createNewFile(); } //创建输出流 FileOutputStream outStream = new FileOutputStream(localFile); //写入数据 outStream.write(data); //关闭输出流 outStream.close(); } catch (Exception e) { e.printStackTrace(); } return fileName; } public byte[] readInputStream(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); //创建一个Buffer字符串 byte[] buffer = new byte[1024]; //每次读取的字符串长度,如果为-1,代表全部读取完毕 int len = 0; //使用一个输入流从buffer里把数据读取出来 while( (len=inStream.read(buffer)) != -1 ){ //用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度 outStream.write(buffer, 0, len); } //关闭输入流 inStream.close(); //把outStream里的数据写入内存 return outStream.toByteArray(); }通过以上代码,就实现了将A中数据下载保存到B中。以上代码也是我从网上找了好久才找到的,加上我自己实际中的需求比如添加代理,设置header等。
最后还要说一下我在这个过程中遇到的坑
1、下载到本地服务器,用户点击链接时,从本地服务器获取链接下载,通过a标签href属性实现,但是会跳转一个空白页面,不会直接下载,当直接刷新空白页面或者将空白页面地址复制到新的标签页访问时就可以将该文件下载下来了???
解决:通过添加tagart属性值为_black,在新的标签页打开连接即可。
2、该功能我在我自己的笔记本上使用eclipse运行调试无误后,部署到linux服务器上,却不能下载文件,错误提示404。几经周折,才明白,是应为A给的代理账号没有创建文件夹和文件的权限。所以文件创建失败。当然也就不可能找到咯,就有了404。
解决:xshell等远程工具,远程到linux服务器上,进入到指定目录,将该目录权限修改为777(sudo chmod 777 指定目录),最后将文件报错到改目录下即可。