root / tags / v2_0_0_Build_2050 / extensions / org.gvsig.installer / org.gvsig.installer.lib / org.gvsig.installer.lib.impl / src / main / java / org / gvsig / installer / lib / impl / utils / Download.java @ 38692
History | View | Annotate | Download (8.63 KB)
1 | 34925 | nfrancisco | /* gvSIG. Geographic Information System of the Valencian Government
|
---|---|---|---|
2 | *
|
||
3 | * Copyright (C) 2007-2008 Infrastructures and Transports Department
|
||
4 | * of the Valencian Government (CIT)
|
||
5 | *
|
||
6 | * This program is free software; you can redistribute it and/or
|
||
7 | * modify it under the terms of the GNU General Public License
|
||
8 | * as published by the Free Software Foundation; either version 2
|
||
9 | * of the License, or (at your option) any later version.
|
||
10 | *
|
||
11 | * This program is distributed in the hope that it will be useful,
|
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 | * GNU General Public License for more details.
|
||
15 | *
|
||
16 | * You should have received a copy of the GNU General Public License
|
||
17 | * along with this program; if not, write to the Free Software
|
||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||
19 | * MA 02110-1301, USA.
|
||
20 | *
|
||
21 | */
|
||
22 | package org.gvsig.installer.lib.impl.utils; |
||
23 | |||
24 | import java.io.BufferedInputStream; |
||
25 | import java.io.BufferedOutputStream; |
||
26 | import java.io.File; |
||
27 | import java.io.FileOutputStream; |
||
28 | import java.io.IOException; |
||
29 | import java.net.URL; |
||
30 | import java.net.URLConnection; |
||
31 | import java.util.Date; |
||
32 | import java.util.StringTokenizer; |
||
33 | |||
34 | import org.slf4j.Logger; |
||
35 | import org.slf4j.LoggerFactory; |
||
36 | |||
37 | 38431 | jldominguez | import org.gvsig.tools.ToolsLocator; |
38 | 34925 | nfrancisco | import org.gvsig.tools.task.AbstractMonitorableTask; |
39 | import org.gvsig.tools.task.SimpleTaskStatus; |
||
40 | 38431 | jldominguez | import org.gvsig.tools.task.TaskStatusManager; |
41 | 34925 | nfrancisco | |
42 | /**
|
||
43 | * @author gvSIG Team
|
||
44 | * @version $Id$
|
||
45 | *
|
||
46 | */
|
||
47 | public class Download extends AbstractMonitorableTask { |
||
48 | |||
49 | 37599 | nfrancisco | private static final Logger LOG = LoggerFactory.getLogger(Download.class); |
50 | 38431 | jldominguez | |
51 | /**
|
||
52 | * If file size provided by URLConnection is smaller than this,
|
||
53 | * then the downloaded file size will always be accepted (unit: bytes)
|
||
54 | */
|
||
55 | private static final int FILE_SIZE_ESTIMATED_MINIMUM = 10000; |
||
56 | |||
57 | /**
|
||
58 | * Downloaded file size will be accepted if it is bigger than this ratio
|
||
59 | * multiplied by the estimated file size provided by URLConnection
|
||
60 | * (unit: bytes)
|
||
61 | */
|
||
62 | private static final double FILE_SIZE_ESTIMATED_RATIO = 0.85; |
||
63 | |||
64 | private boolean selfCreatedTaskStatus = true; |
||
65 | 34967 | nfrancisco | |
66 | 37599 | nfrancisco | /**
|
67 | * @param taskName
|
||
68 | */
|
||
69 | public Download(SimpleTaskStatus taskStatus) {
|
||
70 | this();
|
||
71 | 38431 | jldominguez | |
72 | /*
|
||
73 | * constructor without params is adding
|
||
74 | * itself to manager, so we remove it
|
||
75 | */
|
||
76 | TaskStatusManager manager = ToolsLocator.getTaskStatusManager(); |
||
77 | manager.remove(getTaskStatus()); |
||
78 | |||
79 | /*
|
||
80 | * The received taskstatus is being managed by somebody else,
|
||
81 | * we don not add it to the manager
|
||
82 | */
|
||
83 | 37599 | nfrancisco | this.taskStatus = taskStatus;
|
84 | 38431 | jldominguez | selfCreatedTaskStatus = false;
|
85 | 37599 | nfrancisco | } |
86 | 34925 | nfrancisco | |
87 | 37599 | nfrancisco | public Download() {
|
88 | 38431 | jldominguez | /**
|
89 | * this call is adding the task status to the manager
|
||
90 | */
|
||
91 | 37599 | nfrancisco | super("Downloading..."); |
92 | } |
||
93 | 34967 | nfrancisco | |
94 | 37599 | nfrancisco | public File downloadFile(URL url, String defaultFileName) |
95 | throws IOException { |
||
96 | 34967 | nfrancisco | |
97 | 37599 | nfrancisco | URL downloadURL = url;
|
98 | 34925 | nfrancisco | |
99 | 37599 | nfrancisco | // check if the URL ends with '/' and append the file name
|
100 | if (defaultFileName != null) { |
||
101 | String urlStr = url.toString();
|
||
102 | if (urlStr.endsWith("/")) { |
||
103 | urlStr = urlStr.concat(defaultFileName); |
||
104 | downloadURL = new URL(urlStr); |
||
105 | } |
||
106 | } |
||
107 | 34925 | nfrancisco | |
108 | 37599 | nfrancisco | URLConnection connection = downloadURL.openConnection();
|
109 | 38600 | jldominguez | connection.setConnectTimeout(30000);
|
110 | connection.setReadTimeout(20000);
|
||
111 | |||
112 | 37599 | nfrancisco | String fileName = getFileName(connection);
|
113 | 34925 | nfrancisco | |
114 | 37599 | nfrancisco | if (LOG.isDebugEnabled()) {
|
115 | Date date = new Date(connection.getLastModified()); |
||
116 | LOG |
||
117 | .debug( |
||
118 | "Downloading file {} from URL {}, with last modified date: {}",
|
||
119 | new Object[] { fileName, downloadURL, date }); |
||
120 | } |
||
121 | 34925 | nfrancisco | |
122 | 37599 | nfrancisco | String fileNamePrefix = fileName;
|
123 | String fileNameSuffix = "zip"; |
||
124 | int dotPosition = fileName.lastIndexOf('.'); |
||
125 | if ((dotPosition > -1) && (dotPosition < fileName.length() - 1)) { |
||
126 | fileNamePrefix = fileName.substring(0, dotPosition);
|
||
127 | fileNameSuffix = fileName.substring(dotPosition); |
||
128 | } |
||
129 | 34925 | nfrancisco | |
130 | 38600 | jldominguez | BufferedInputStream bis = new BufferedInputStream(connection.getInputStream()); |
131 | 34925 | nfrancisco | |
132 | 37599 | nfrancisco | File localFile = File.createTempFile(fileNamePrefix, fileNameSuffix); |
133 | 34967 | nfrancisco | |
134 | 37599 | nfrancisco | BufferedOutputStream bos = new BufferedOutputStream( |
135 | new FileOutputStream(localFile)); |
||
136 | 34925 | nfrancisco | |
137 | 38431 | jldominguez | int expected_size = connection.getContentLength();
|
138 | this.taskStatus.setRangeOfValues(0, expected_size); |
||
139 | 38435 | jldominguez | |
140 | |||
141 | 37599 | nfrancisco | byte[] data = new byte[1024]; |
142 | int count = 0; |
||
143 | 38431 | jldominguez | long totalCount = 0; // total bytes read |
144 | 37599 | nfrancisco | while ((count = bis.read(data, 0, 1024)) >= 0) { |
145 | 38431 | jldominguez | |
146 | 37599 | nfrancisco | bos.write(data, 0, count);
|
147 | totalCount += count; |
||
148 | 38431 | jldominguez | |
149 | 37599 | nfrancisco | this.taskStatus.setCurValue(totalCount);
|
150 | 34925 | nfrancisco | |
151 | 37599 | nfrancisco | if (this.taskStatus.isCancellationRequested()) { |
152 | 38431 | jldominguez | break;
|
153 | 37599 | nfrancisco | } |
154 | } |
||
155 | 38431 | jldominguez | |
156 | 38435 | jldominguez | try {
|
157 | bis.close(); |
||
158 | bos.flush(); |
||
159 | bos.close(); |
||
160 | } catch (Exception ex) { |
||
161 | LOG.info("Error while closing download streams: " + ex.getMessage());
|
||
162 | } |
||
163 | |||
164 | 38431 | jldominguez | if (selfCreatedTaskStatus) {
|
165 | 37599 | nfrancisco | this.taskStatus.terminate();
|
166 | this.taskStatus.remove();
|
||
167 | } |
||
168 | 38431 | jldominguez | |
169 | // out from the read loop
|
||
170 | // perhaps it was cancelled:
|
||
171 | if (this.taskStatus.isCancellationRequested()) { |
||
172 | return null; |
||
173 | } |
||
174 | 38435 | jldominguez | |
175 | 38540 | jldominguez | String md5_ = MD5BinaryFileUtils.getMD5InRemoteFile(downloadURL);
|
176 | 38435 | jldominguez | |
177 | if (md5_ != null) { |
||
178 | // ==========================================
|
||
179 | // check md5
|
||
180 | String local_md5 = null; |
||
181 | try {
|
||
182 | 38540 | jldominguez | local_md5 = MD5BinaryFileUtils.getMD5Checksum(localFile); |
183 | 38435 | jldominguez | } catch (Exception e) { |
184 | throw new IOException("Unable to get MD5 for file: " + downloadURL.toString()); |
||
185 | } |
||
186 | if (local_md5.compareTo(md5_) != 0) { |
||
187 | throw new IOException("MD5 does not match for file: " + downloadURL.toString()); |
||
188 | } |
||
189 | // ==========================================
|
||
190 | } else {
|
||
191 | // ==========================================
|
||
192 | // check real size and expected size:
|
||
193 | if (!acceptableFileSize(localFile, expected_size)) {
|
||
194 | throw new IOException("Bad download file size (" |
||
195 | + localFile.length() |
||
196 | + " / "
|
||
197 | + expected_size |
||
198 | + ")");
|
||
199 | } |
||
200 | // ==========================================
|
||
201 | 38431 | jldominguez | } |
202 | |||
203 | // everything seems to be OK
|
||
204 | 37599 | nfrancisco | return localFile;
|
205 | 34925 | nfrancisco | |
206 | 37599 | nfrancisco | } |
207 | 34925 | nfrancisco | |
208 | 37599 | nfrancisco | /**
|
209 | * Returns the file name associated to an url connection.<br />
|
||
210 | * The result is not a path but just a file name.
|
||
211 | *
|
||
212 | * @param urlConnection
|
||
213 | * - the url connection
|
||
214 | * @return the file name
|
||
215 | *
|
||
216 | * @throws IOException
|
||
217 | * Signals that an I/O exception has occurred.
|
||
218 | */
|
||
219 | private String getFileName(URLConnection urlConnection) throws IOException { |
||
220 | String fileName = null; |
||
221 | 34925 | nfrancisco | |
222 | 37599 | nfrancisco | String contentDisposition = urlConnection
|
223 | .getHeaderField("content-disposition");
|
||
224 | 34925 | nfrancisco | |
225 | 37599 | nfrancisco | if (contentDisposition != null) { |
226 | fileName = extractFileNameFromContentDisposition(contentDisposition); |
||
227 | } |
||
228 | 34925 | nfrancisco | |
229 | 37599 | nfrancisco | // if the file name cannot be extracted from the content-disposition
|
230 | // header, using the url.getFilename() method
|
||
231 | if (fileName == null) { |
||
232 | StringTokenizer st = new StringTokenizer(urlConnection.getURL() |
||
233 | .getFile(), "/");
|
||
234 | while (st.hasMoreTokens()) {
|
||
235 | fileName = st.nextToken(); |
||
236 | } |
||
237 | } |
||
238 | 34925 | nfrancisco | |
239 | 37599 | nfrancisco | return fileName;
|
240 | } |
||
241 | 34925 | nfrancisco | |
242 | 37599 | nfrancisco | /**
|
243 | * Extract the file name from the content disposition header.
|
||
244 | * <p>
|
||
245 | * See <a
|
||
246 | * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html">http:
|
||
247 | * //www.w3.org/Protocols/rfc2616/rfc2616-sec19.html</a> for detailled
|
||
248 | * information regarding the headers in HTML.
|
||
249 | *
|
||
250 | * @param contentDisposition
|
||
251 | * - the content-disposition header. Cannot be <code>null>/code>.
|
||
252 | * @return the file name, or <code>null</code> if the content-disposition
|
||
253 | * header does not contain the filename attribute.
|
||
254 | */
|
||
255 | private String extractFileNameFromContentDisposition( |
||
256 | String contentDisposition) {
|
||
257 | String[] attributes = contentDisposition.split(";"); |
||
258 | 34925 | nfrancisco | |
259 | 37599 | nfrancisco | for (String a : attributes) { |
260 | if (a.toLowerCase().contains("filename")) { |
||
261 | // The attribute is the file name. The filename is between
|
||
262 | // quotes.
|
||
263 | return a.substring(a.indexOf('\"') + 1, a.lastIndexOf('\"')); |
||
264 | } |
||
265 | } |
||
266 | 34925 | nfrancisco | |
267 | 37599 | nfrancisco | // not found
|
268 | return null; |
||
269 | 34925 | nfrancisco | |
270 | 37599 | nfrancisco | } |
271 | 38431 | jldominguez | |
272 | /**
|
||
273 | * {@link URLConnection}'s method getContentLength() does not give
|
||
274 | * exact size.
|
||
275 | *
|
||
276 | * @return whether the size is acceptable
|
||
277 | */
|
||
278 | public static boolean acceptableFileSize(File f, int estimated_size) { |
||
279 | |||
280 | if (f == null) { |
||
281 | return false; |
||
282 | } |
||
283 | |||
284 | if (estimated_size == -1) { |
||
285 | // -1 means unknown
|
||
286 | return true; |
||
287 | } |
||
288 | |||
289 | if (estimated_size < FILE_SIZE_ESTIMATED_MINIMUM) {
|
||
290 | return true; |
||
291 | } |
||
292 | |||
293 | return f.length() > (FILE_SIZE_ESTIMATED_RATIO * estimated_size);
|
||
294 | } |
||
295 | 34925 | nfrancisco | } |