Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.installer / org.gvsig.installer.lib / org.gvsig.installer.lib.impl / src / main / java / org / gvsig / installer / lib / impl / utils / Download.java @ 43345

History | View | Annotate | Download (8.55 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.installer.lib.impl.utils;
25

    
26
import java.io.BufferedInputStream;
27
import java.io.BufferedOutputStream;
28
import java.io.File;
29
import java.io.FileOutputStream;
30
import java.io.IOException;
31
import java.net.URL;
32
import java.net.URLConnection;
33
import java.util.Date;
34
import java.util.StringTokenizer;
35

    
36
import org.slf4j.Logger;
37
import org.slf4j.LoggerFactory;
38

    
39
import org.gvsig.tools.ToolsLocator;
40
import org.gvsig.tools.folders.FoldersManager;
41
import org.gvsig.tools.task.AbstractMonitorableTask;
42
import org.gvsig.tools.task.SimpleTaskStatus;
43
import org.gvsig.tools.task.TaskStatusManager;
44

    
45
/**
46
 * @author gvSIG Team
47
 * @version $Id$
48
 * 
49
 */
50
public class Download extends AbstractMonitorableTask {
51

    
52
        private static final Logger LOG = LoggerFactory.getLogger(Download.class);
53
        
54
        /**
55
         * If file size provided by URLConnection is smaller than this,
56
         * then the downloaded file size will always be accepted (unit: bytes)
57
         */
58
    private static final int FILE_SIZE_ESTIMATED_MINIMUM = 10000;
59
    
60
    /**
61
     * Downloaded file size will be accepted if it is bigger than this ratio
62
     * multiplied by the estimated file size provided by URLConnection
63
     * (unit: bytes)
64
     */
65
    private static final double FILE_SIZE_ESTIMATED_RATIO = 0.85;
66
    
67
        private boolean selfCreatedTaskStatus = true;
68

    
69
        /**
70
         * @param taskName
71
         */
72
        public Download(SimpleTaskStatus taskStatus) {
73
                this();
74
                
75
                /*
76
                 * constructor without params is adding
77
                 * itself to manager, so we remove it
78
                 */
79
        TaskStatusManager manager = ToolsLocator.getTaskStatusManager();
80
        manager.remove(getTaskStatus());
81
        
82
        /*
83
         * The received taskstatus is being managed by somebody else,
84
         * we don not add it to the manager
85
         */
86
                this.taskStatus = taskStatus;
87
                selfCreatedTaskStatus = false;
88
        }
89

    
90
        public Download() {
91
            /**
92
             * this call is adding the task status to the manager
93
             */
94
                super("Downloading...");
95
        }
96

    
97
        public File downloadFile(URL url, String defaultFileName)
98
                        throws IOException {
99

    
100
                URL downloadURL = url;
101

    
102
                // check if the URL ends with '/' and append the file name
103
                if (defaultFileName != null) {
104
                        String urlStr = url.toString();
105
                        if (urlStr.endsWith("/")) {
106
                                urlStr = urlStr.concat(defaultFileName);
107
                                downloadURL = new URL(urlStr);
108
                        }
109
                }
110

    
111
                URLConnection connection = downloadURL.openConnection();
112
                connection.setUseCaches(false);
113
                connection.setConnectTimeout(30000);
114
                connection.setReadTimeout(20000);
115
                
116
                String fileName = getFileName(connection);
117

    
118
                // if (LOG.isDebugEnabled()) {
119
                        Date date = new Date(connection.getLastModified());
120
                        LOG.info(
121
                "Downloading file {} from URL {}, with last modified date: {}",
122
                new Object[] { fileName, downloadURL, date }
123
            );
124
                // }
125

    
126
        FoldersManager foldersManager = ToolsLocator.getFoldersManager();
127
                File localFile = foldersManager.getUniqueTemporaryFile(fileName);
128

    
129
                BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
130
                BufferedOutputStream bos = new BufferedOutputStream(
131
            new FileOutputStream(localFile)
132
        );
133
                int expected_size = connection.getContentLength();
134
                this.taskStatus.setRangeOfValues(0, expected_size);
135

    
136
        byte[] data = new byte[1024];
137
        int count = 0;
138
        long totalCount = 0; // total bytes read
139
        while ((count = bis.read(data, 0, 1024)) >= 0) {
140

    
141
            bos.write(data, 0, count);
142
            totalCount += count;
143

    
144
            this.taskStatus.setCurValue(totalCount);
145

    
146
            if (this.taskStatus.isCancellationRequested()) {
147
                break;
148
            }
149
        }
150

    
151
        try {
152
             bis.close();
153
             bos.flush();
154
             bos.close();
155
        } catch (Exception ex) {
156
            LOG.info("Error while closing download streams: " + ex.getMessage());
157
        }
158

    
159
        if (selfCreatedTaskStatus) {
160
            this.taskStatus.terminate();
161
            this.taskStatus.remove();
162
        }
163

    
164
        // out from the read loop
165
        // perhaps it was cancelled:
166
        if (this.taskStatus.isCancellationRequested()) {
167
            return null;
168
        }
169

    
170
        String md5_ = MD5BinaryFileUtils.getMD5InRemoteFile(downloadURL);
171

    
172
        if (md5_ != null) {
173
            // ==========================================
174
            // check md5
175
            String local_md5 = null;
176
            try {
177
                local_md5 = MD5BinaryFileUtils.getMD5Checksum(localFile);
178
            } catch (Exception e) {
179
                throw new IOException("Unable to get MD5 for file: " + downloadURL.toString());
180
            }
181
            if (local_md5.compareTo(md5_) != 0) {
182
                throw new IOException("MD5 does not match for file: " + downloadURL.toString());
183
            }
184
            // ==========================================
185
        } else {
186
            // ==========================================
187
            // check real size and expected size:
188
            if (!acceptableFileSize(localFile, expected_size)) {
189
                throw new IOException("Bad download file size ("
190
                    + localFile.length()
191
                    + " / "
192
                    + expected_size
193
                    + ")");
194
            }
195
            // ==========================================
196
        }
197

    
198
        // everything seems to be OK
199
        return localFile;
200

    
201
        }
202

    
203
        /**
204
         * Returns the file name associated to an url connection.<br />
205
         * The result is not a path but just a file name.
206
         * 
207
         * @param urlConnection
208
         *            - the url connection
209
         * @return the file name
210
         * 
211
         * @throws IOException
212
         *             Signals that an I/O exception has occurred.
213
         */
214
        private String getFileName(URLConnection urlConnection) throws IOException {
215
                String fileName = null;
216

    
217
                String contentDisposition = urlConnection
218
                                .getHeaderField("content-disposition");
219

    
220
                if (contentDisposition != null) {
221
                        fileName = extractFileNameFromContentDisposition(contentDisposition);
222
                }
223

    
224
                // if the file name cannot be extracted from the content-disposition
225
                // header, using the url.getFilename() method
226
                if (fileName == null) {
227
                        StringTokenizer st = new StringTokenizer(urlConnection.getURL()
228
                                        .getFile(), "/");
229
                        while (st.hasMoreTokens()) {
230
                                fileName = st.nextToken();
231
                        }
232
                }
233

    
234
                return fileName;
235
        }
236

    
237
        /**
238
         * Extract the file name from the content disposition header.
239
         * <p>
240
         * See <a
241
         * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html">http:
242
         * //www.w3.org/Protocols/rfc2616/rfc2616-sec19.html</a> for detailled
243
         * information regarding the headers in HTML.
244
         * 
245
         * @param contentDisposition
246
         *            - the content-disposition header. Cannot be <code>null>/code>.
247
         * @return the file name, or <code>null</code> if the content-disposition
248
         *         header does not contain the filename attribute.
249
         */
250
        private String extractFileNameFromContentDisposition(
251
                        String contentDisposition) {
252
                String[] attributes = contentDisposition.split(";");
253

    
254
                for (String a : attributes) {
255
                        if (a.toLowerCase().contains("filename")) {
256
                                // The attribute is the file name. The filename is between
257
                                // quotes.
258
                                return a.substring(a.indexOf('\"') + 1, a.lastIndexOf('\"'));
259
                        }
260
                }
261

    
262
                // not found
263
                return null;
264

    
265
        }
266
        
267
        /**
268
         * {@link URLConnection}'s method getContentLength() does not give
269
         * exact size.
270
         * 
271
         * @return whether the size is acceptable
272
         */
273
        public static boolean acceptableFileSize(File f, int estimated_size) {
274
            
275
            if (f == null) {
276
                return false;
277
            }
278
            
279
            if (estimated_size == -1) {
280
                // -1 means unknown
281
                return true;
282
            }
283
            
284
            if (estimated_size < FILE_SIZE_ESTIMATED_MINIMUM) {
285
                return true;
286
            }
287
            
288
            return f.length() > (FILE_SIZE_ESTIMATED_RATIO * estimated_size);
289
        }
290
}