前言

根据URL解析HTML获取文件URL并下载存储

最近公司接入了一个平台的数据,给了一个连接,存放每天定时推的文件列表。我这里需要做的就是我要把这些文件下载下来,保存到服务器上,其他人那它去用。

正文

一、URL内容

提供的URL使用用户名和密码登录进去长这个样子。这个是<body></body>部分,我主要处理这个部分。

事实上他这个标签实在是不规律。只有后面的文件名使用<a></a>包括。所以只能对这个文件名做文章。

二、需求

每次定时访问这个URL,需要下载自己服务器上的存放位置没有的最新文件,那么需要一个记录值来记录最后下载的一个位置。

当是首次下载,那么没有这个记录值的时候,我只需要下载最新文件中后缀是“f”的压缩包,然后记录这个值。

当是后续下载,需要根据记录值下载它后面的所有文件,然后记录这个值。

二、使用步骤

1.引入库

URL访问和解析使用dom4j的相关东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.49</version>
</dependency>

2.贴代码(长代码警告)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
public class FileUtils {

private static final Logger logger = LoggerFactory.getLogger(DowJonesFileUtils.class);

private static InputStream inputStream;
private static BufferedInputStream bi;
private static HttpURLConnection conn;
private static URL httpUrl;
private static OutputStream outputStream;
private static BufferedOutputStream bo;

//需要登录的网站
private static String baseURL = "https://*******.com";
//记录值文件
private static String recordFile = "record.txt";
//文件存放位置
private static String dirPath = "/data/******";

//定时任务每四个小时执行一次
@Scheduled(cron = "0 0/4 * * * ?")
public void operate() throws IOException {
logger.info("文件下载开始");
//登录
FileUtils jones = new FileUtils();
Connection.Response login = jones.login();
logger.info("登录成功");

//解析
Elements pre_a = jones.parse(login);
if (pre_a ==null){
return;
}

//获取记录的文件信息,确定下载位置
File file = new File(dirPath+"/"+recordFile);
//把记录位置以后的文件,作为要下载的文件,集合
List<String> downURL = jones.getDownURL(pre_a, file);
if (downURL.size()==0){
logger.info("没有需要下载的文件");
logger.info("文件下载结束");
return;
}
logger.info("开始下载,剩余文件数量:"+downURL.size());
//下载文件
downFile(downURL);
logger.info("文件下载结束");
}

/**
* @Description: 登录
* @param
* @return org.jsoup.Connection.Response
* @throws
* @author Surpass
* @date 2020/10/15 11:11
*/
private Connection.Response login(){
Connection.Response login = null;
try {
login = Jsoup.connect("https://***************/XML/1815")
.data("Username","****") //账号
.data("Password","********") //密码
.header("Authorization","*******") //url网站访问权限 请求头里
.method(Connection.Method.GET)
.execute();
} catch (IOException e) {
logger.info("登录异常:",e);
}
return login;
}


/**
* @Description: 登录解析html获取文件主体
* @param login
* @return org.jsoup.select.Elements
* @throws
* @author Surpass
* @date 2020/10/15 11:09
*/
private Elements parse(Connection.Response login){
Document document = null;
if (login ==null){
return null;
}
try {
document = login.parse();
} catch (IOException e) {
logger.info("解析异常:",e);
}
//根据html代码选择获取了所有的链接标签
Elements pre_a = document.select("pre a");
return pre_a;
}

/**
* @Description: 根据标签ELe获取需要加载的URL
* @param pre_a, 标签ELes
* baseURL, url前缀
* file 记录文件
* @return java.util.List<java.lang.String>
* @throws
* @author Surpass
* @date 2020/10/14 18:40
*/
private List<String> getDownURL(Elements pre_a,File file) throws IOException {
//获取记录文件中的记录时间和全、增量标识
Double recordData = null;
char recordType = 0;
String record = null;
if (!file.exists()){
file.createNewFile();
}else {
//获取记录文件中的记录值
Reader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
record = br.readLine();
if (record!=null&&record.trim()!=""){
String[] split = record.split("_");
recordData = Double.valueOf(split[0]);
recordType = split[1].charAt(0);
}
if (br!=null){
br.close();
}
if (reader!=null){
reader.close();
}
}

ArrayList<String> list = new ArrayList<>();
//标签中的text
List<String> textList = pre_a.eachText();
//标签中的属性
List<String> hrefList = pre_a.eachAttr("href");
//获取记录值之后的文件URL,存入集合
for (int i = 0; i < hrefList.size(); i++) {
String href = hrefList.get(i);
String downURL = baseURL+href;

String text = textList.get(i);
//URL过滤
if (!text.endsWith(".zip")){
continue;
}
//当有记录值的时候,下载记录值以后的所有文件
if (StringUtils.isNotBlank(record)){
String[] textArr = text.split("_");
Double fileData = Double.valueOf(textArr[4]);
//当前值和记录值相同
if (fileData.equals(recordData)){
if ( recordType=='f' && textArr[5].charAt(0)=='i' ){
list.add(downURL);
}
}
if (fileData>recordData){
list.add(downURL);
}
}
//当没有记录值的时候,只下载最新的全量(当倒数第二个是全量,那么直接跳出循环)
if (StringUtils.isBlank(record)){
char flag = textList.get(i).charAt(textList.get(i).length() - 5);
if (i==hrefList.size()-2&&flag=='f'){
list.add(downURL);
break;
}
if (i==hrefList.size()-1&&flag=='f'){
list.add(downURL);
}
}
}
logger.info("需要下载的URL数量:"+list.size());
return list;
}


/**
* @Description: 这里是重点了,拿到了需要获取的URL,请求下载并保存
* @param downURL, 需要下载的文件url
* dirPath, 保存文件路径
* recordPath 记录文件的位置
* @return void
* @throws
* @author Surpass
* @date 2020/10/15 11:09
*/
private void downFile(List<String> downURL) {
int urlFlag = 0;
try {
for (String url : downURL) {
//文件下载登录
httpUrl=new URL(url);
conn=(HttpURLConnection) httpUrl.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization","*******");
conn.connect();
logger.info(url+":连接成功");

//获取输入流
inputStream = conn.getInputStream();
bi = new BufferedInputStream(inputStream);
String fileName = url.substring(url.lastIndexOf("/"));
//获取输出流
outputStream = new FileOutputStream(dirPath+fileName);
bo = new BufferedOutputStream(outputStream);
byte[] bytes = new byte[1024*10];
int len = 0;
while ( (len = bi.read(bytes))!=-1){
bo.write(bytes,0,len);
}
logger.info("上传URL文件到服务器成功:"+url);

//记录当前下载位置
String record = url.substring(69, 83);
Writer recordWriter = new FileWriter(dirPath+"/"+recordFile);
BufferedWriter recordBf = new BufferedWriter(recordWriter);
recordBf.write(record);
logger.info("上传记录文件record.txt成功");
//关闭
recordBf.flush();
recordBf.close();
recordWriter.close();

++urlFlag;
logger.info("下载完成,剩余文件数量:"+(downURL.size()-urlFlag));
}
}catch (Exception e){
logger.info("下载失败",e);
}finally {
//关闭流
try {
if (bo!=null){
bo.flush();
bo.close();
}
if (inputStream!=null){
inputStream.close();
}
if (bi!=null){
bi.close();
}
if (outputStream!=null){
outputStream.close();
}

if (conn!=null){
conn.disconnect();
}
} catch (IOException e) {
logger.info("输入流关闭异常",e);
}
}
}
}

3.下载的文件

总结

这一次这个任务结合了网页的解析,用Java下载文件,输入输出流等一些东西,本来文件是存放在sftp服务器的,后来需求改了。这是第一次在项目中使用网页解析,博主会在代码中加很多注释。这里记录下,可作为参考。

当然,代码还存在很多需要完善的地方,还请各位大佬指出不足,后续加倍努力。