├── LICENSE.txt ├── README.textile ├── pom.xml └── src ├── main └── java │ └── org │ └── kamranzafar │ └── jddl │ ├── Authentication.java │ ├── DirectDownloader.java │ ├── DownloadAdaptor.java │ ├── DownloadListener.java │ ├── DownloadTask.java │ ├── HttpConnector.java │ └── util │ └── Base64.java └── test └── java └── org └── kamranzafar └── jddl ├── AndroidExample.java ├── SimpleTest.java └── SwingTest.java /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. Overview 2 | 3 | jddl (Java Direct Downloader) is a simple and lightweight java library, which makes it very easy and trivial to direct-download multiple files over HTTP and to track the download progress. Now supports SSL and basic authentication. 4 | 5 | Note: Support for FTP downloads will be added in future releases 6 | 7 | h1. Usage 8 | 9 | jddl is a multi-threaded API capable of parallel-downloading multiple files and uses a custom event handler to update the download status. Below is a basic example. 10 | 11 |
12 |
13 | // Create a DirectDownloader instance
14 | DirectDownloader dd = new DirectDownloader();
15 |
16 | String file = "http://someserver/somefile.zip"
17 | String out = "somefile.zip"
18 |
19 | // Add files to be downloaded
20 | dd.download( new DownloadTask( new URL( file ), new FileOutputStream( out ), new DownloadListener() {
21 | String fname;
22 |
23 | public void onUpdate(int bytes, int totalDownloaded) {
24 | // update progress bar etc.
25 | }
26 |
27 | public void onStart(String fname, int size) {
28 | this.fname = fname;
29 | System.out.println( "Downloading " + fname + " of size " + size );
30 | }
31 |
32 | public void onComplete() {
33 | System.out.println( fname + " downloaded" );
34 | }
35 |
36 | public void onCancel() {
37 | System.out.println( fname + " cancelled" );
38 | }
39 | } ) );
40 |
41 | // Start downloading
42 | Thread t = new Thread( dd );
43 | t.start();
44 | t.join();
45 |
46 |
47 |
48 | h1. Examples
49 |
50 | Below are some comprehensive examples of using jddl.
51 |
52 | * "Console example":https://github.com/kamranzafar/jddl/blob/master/src/test/java/org/kamranzafar/jddl/SimpleTest.java
53 | * "Swing example":https://github.com/kamranzafar/jddl/blob/master/src/test/java/org/kamranzafar/jddl/SwingTest.java
54 | * "Android example":https://github.com/kamranzafar/jddl/blob/master/src/test/java/org/kamranzafar/jddl/AndroidExample.java
55 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 | 22 | * Encodes and decodes to and from Base64 notation. 23 | *
24 | *25 | * Homepage: http://iharder.net/base64. 26 | *
27 | * 28 | *29 | * Example: 30 | *
31 | * 32 | *String encoded = Base64.encode( myByteArray );
byte[] myByteArray = Base64.decode( encoded );
34 | *
35 | * 36 | * The options parameter, which appears in a few places, is used to 37 | * pass several pieces of information to the encoder. In the "higher level" 38 | * methods such as encodeBytes( bytes, options ) the options parameter can be 39 | * used to indicate such things as first gzipping the bytes before encoding 40 | * them, not inserting linefeeds, and encoding using the URL-safe and Ordered 41 | * dialects. 42 | *
43 | * 44 | *45 | * Note, according to RFC3548, Section 2.1, 47 | * implementations should not add line feeds unless explicitly told to do so. 48 | * I've got Base64 set to this behavior now, although earlier versions broke 49 | * lines by default. 50 | *
51 | * 52 | *53 | * The constants defined in Base64 can be OR-ed together to combine options, so 54 | * you might make a call like this: 55 | *
56 | * 57 | *String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );
58 | * 59 | * to compress the data before encoding it and then making the output have 60 | * newline characters. 61 | *
62 | *63 | * Also... 64 | *
65 | *String encoded = Base64.encodeBytes( crazyString.getBytes() );
66 | *
67 | *
68 | *
69 | * 70 | * Change Log: 71 | *
72 | *178 | * I am placing this code in the Public Domain. Do with it as you will. This 179 | * software comes with no guarantees or warranties but with plenty of 180 | * well-wishing instead! Please visit http://iharder.net/base64 periodically 182 | * to check for updates or to contribute improvements. 183 | *
184 | * 185 | * @author Robert Harder 186 | * @author rob@iharder.net 187 | * @version 2.3.7 188 | */ 189 | public class Base64 { 190 | 191 | /* ******** P U B L I C F I E L D S ******** */ 192 | 193 | /** No options specified. Value is zero. */ 194 | public final static int NO_OPTIONS = 0; 195 | 196 | /** Specify encoding in first bit. Value is one. */ 197 | public final static int ENCODE = 1; 198 | 199 | /** Specify decoding in first bit. Value is zero. */ 200 | public final static int DECODE = 0; 201 | 202 | /** Specify that data should be gzip-compressed in second bit. Value is two. */ 203 | public final static int GZIP = 2; 204 | 205 | /** 206 | * Specify that gzipped data should not be automatically gunzipped. 207 | */ 208 | public final static int DONT_GUNZIP = 4; 209 | 210 | /** Do break lines when encoding. Value is 8. */ 211 | public final static int DO_BREAK_LINES = 8; 212 | 213 | /** 214 | * Encode using Base64-like encoding that is URL- and Filename-safe as 215 | * described in Section 4 of RFC3548: http://www.faqs.org/rfcs/rfc3548.html. It is important to note that 218 | * data encoded this way is not officially valid Base64, or at the 219 | * very least should not be called Base64 without also specifying that is 220 | * was encoded using the URL- and Filename-safe dialect. 221 | */ 222 | public final static int URL_SAFE = 16; 223 | 224 | /** 225 | * Encode using the special "ordered" dialect of Base64 described here: http://www.faqs.org/qa/rfcc- 227 | * 1940.html. 228 | */ 229 | public final static int ORDERED = 32; 230 | 231 | /* ******** P R I V A T E F I E L D S ******** */ 232 | 233 | /** Maximum line length (76) of Base64 output. */ 234 | private final static int MAX_LINE_LENGTH = 76; 235 | 236 | /** The equals sign (=) as a byte. */ 237 | private final static byte EQUALS_SIGN = (byte) '='; 238 | 239 | /** The new line character (\n) as a byte. */ 240 | private final static byte NEW_LINE = (byte) '\n'; 241 | 242 | /** Preferred encoding. */ 243 | private final static String PREFERRED_ENCODING = "US-ASCII"; 244 | 245 | private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in 246 | // encoding 247 | private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in 248 | // encoding 249 | 250 | /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ 251 | 252 | /** The 64 valid Base64 values. */ 253 | /* 254 | * Host platform me be something funny like EBCDIC, so we hardcode these 255 | * values. 256 | */ 257 | private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', 258 | (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', 259 | (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', 260 | (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', 261 | (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', 262 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', 263 | (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', 264 | (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' }; 265 | 266 | /** 267 | * Translates a Base64 value to either its 6-bit reconstruction value or a 268 | * negative number indicating some other meaning. 269 | **/ 270 | private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 271 | // 0 272 | // - 273 | // 8 274 | -5, -5, // Whitespace: Tab and Linefeed 275 | -9, -9, // Decimal 11 - 12 276 | -5, // Whitespace: Carriage Return 277 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 278 | // 26 279 | -9, -9, -9, -9, -9, // Decimal 27 - 31 280 | -5, // Whitespace: Space 281 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 282 | 62, // Plus sign at decimal 43 283 | -9, -9, -9, // Decimal 44 - 46 284 | 63, // Slash at decimal 47 285 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine 286 | -9, -9, -9, // Decimal 58 - 60 287 | -1, // Equals sign at decimal 61 288 | -9, -9, -9, // Decimal 62 - 64 289 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 290 | // 'N' 291 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' 292 | // through 'Z' 293 | -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 294 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' 295 | // through 'm' 296 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' 297 | // through 'z' 298 | -9, -9, -9, -9, -9 // Decimal 123 - 127 299 | , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 300 | // 139 301 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 302 | // 152 303 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 304 | // 165 305 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 306 | // 178 307 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 308 | // 191 309 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 310 | // 204 311 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 312 | // 217 313 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 314 | // 230 315 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 316 | // 243 317 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 318 | }; 319 | 320 | /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ 321 | 322 | /** 323 | * Used in the URL- and Filename-safe dialect described in Section 4 of 324 | * RFC3548: http://www.faqs.org 326 | * /rfcs/rfc3548.html. Notice that the last two bytes become "hyphen" 327 | * and "underscore" instead of "plus" and "slash." 328 | */ 329 | private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', 330 | (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', 331 | (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', 332 | (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', 333 | (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', 334 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', 335 | (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', 336 | (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' }; 337 | 338 | /** 339 | * Used in decoding URL- and Filename-safe dialects of Base64. 340 | */ 341 | private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 342 | // 0 343 | // - 344 | // 8 345 | -5, -5, // Whitespace: Tab and Linefeed 346 | -9, -9, // Decimal 11 - 12 347 | -5, // Whitespace: Carriage Return 348 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 349 | // 26 350 | -9, -9, -9, -9, -9, // Decimal 27 - 31 351 | -5, // Whitespace: Space 352 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 353 | -9, // Plus sign at decimal 43 354 | -9, // Decimal 44 355 | 62, // Minus sign at decimal 45 356 | -9, // Decimal 46 357 | -9, // Slash at decimal 47 358 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine 359 | -9, -9, -9, // Decimal 58 - 60 360 | -1, // Equals sign at decimal 61 361 | -9, -9, -9, // Decimal 62 - 64 362 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 363 | // 'N' 364 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' 365 | // through 'Z' 366 | -9, -9, -9, -9, // Decimal 91 - 94 367 | 63, // Underscore at decimal 95 368 | -9, // Decimal 96 369 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' 370 | // through 'm' 371 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' 372 | // through 'z' 373 | -9, -9, -9, -9, -9 // Decimal 123 - 127 374 | , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 375 | // 139 376 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 377 | // 152 378 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 379 | // 165 380 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 381 | // 178 382 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 383 | // 191 384 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 385 | // 204 386 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 387 | // 217 388 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 389 | // 230 390 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 391 | // 243 392 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 393 | }; 394 | 395 | /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ 396 | 397 | /** 398 | * I don't get the point of this technique, but someone requested it, and it 399 | * is described here: http:// 401 | * www.faqs.org/qa/rfcc-1940.html. 402 | */ 403 | private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3', 404 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', 405 | (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', 406 | (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', 407 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', 408 | (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', 409 | (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', 410 | (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' }; 411 | 412 | /** 413 | * Used in decoding the "ordered" dialect of Base64. 414 | */ 415 | private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 416 | // 0 417 | // - 418 | // 8 419 | -5, -5, // Whitespace: Tab and Linefeed 420 | -9, -9, // Decimal 11 - 12 421 | -5, // Whitespace: Carriage Return 422 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 423 | // 26 424 | -9, -9, -9, -9, -9, // Decimal 27 - 31 425 | -5, // Whitespace: Space 426 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 427 | -9, // Plus sign at decimal 43 428 | -9, // Decimal 44 429 | 0, // Minus sign at decimal 45 430 | -9, // Decimal 46 431 | -9, // Slash at decimal 47 432 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine 433 | -9, -9, -9, // Decimal 58 - 60 434 | -1, // Equals sign at decimal 61 435 | -9, -9, -9, // Decimal 62 - 64 436 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' 437 | // through 'M' 438 | 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' 439 | // through 'Z' 440 | -9, -9, -9, -9, // Decimal 91 - 94 441 | 37, // Underscore at decimal 95 442 | -9, // Decimal 96 443 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' 444 | // through 'm' 445 | 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' 446 | // through 'z' 447 | -9, -9, -9, -9, -9 // Decimal 123 - 127 448 | , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 449 | // - 139 450 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 451 | // 152 452 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 453 | // 165 454 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 455 | // 178 456 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 457 | // 191 458 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 459 | // 204 460 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 461 | // 217 462 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 463 | // 230 464 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 465 | // 243 466 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 467 | }; 468 | 469 | /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ 470 | 471 | /** 472 | * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the 473 | * options specified. It's possible, though silly, to specify ORDERED 474 | * and URLSAFE in which case one of them will be picked, though there 475 | * is no guarantee as to which one will be picked. 476 | */ 477 | private final static byte[] getAlphabet(int options) { 478 | if ((options & URL_SAFE) == URL_SAFE) { 479 | return _URL_SAFE_ALPHABET; 480 | } else if ((options & ORDERED) == ORDERED) { 481 | return _ORDERED_ALPHABET; 482 | } else { 483 | return _STANDARD_ALPHABET; 484 | } 485 | } // end getAlphabet 486 | 487 | /** 488 | * Returns one of the _SOMETHING_DECODABET byte arrays depending on the 489 | * options specified. It's possible, though silly, to specify ORDERED and 490 | * URL_SAFE in which case one of them will be picked, though there is no 491 | * guarantee as to which one will be picked. 492 | */ 493 | private final static byte[] getDecodabet(int options) { 494 | if ((options & URL_SAFE) == URL_SAFE) { 495 | return _URL_SAFE_DECODABET; 496 | } else if ((options & ORDERED) == ORDERED) { 497 | return _ORDERED_DECODABET; 498 | } else { 499 | return _STANDARD_DECODABET; 500 | } 501 | } // end getAlphabet 502 | 503 | /** Defeats instantiation. */ 504 | private Base64() { 505 | } 506 | 507 | /* ******** E N C O D I N G M E T H O D S ******** */ 508 | 509 | /** 510 | * Encodes up to the first three bytes of array threeBytes and 511 | * returns a four-byte array in Base64 notation. The actual number of 512 | * significant bytes in your array is given by numSigBytes. The 513 | * array threeBytes needs only be as big as 514 | * numSigBytes. Code can reuse a byte array by passing a 515 | * four-byte array as b4. 516 | * 517 | * @param b4 518 | * A reusable byte array to reduce array instantiation 519 | * @param threeBytes 520 | * the array to convert 521 | * @param numSigBytes 522 | * the number of significant bytes in your array 523 | * @return four byte array in Base64 notation. 524 | * @since 1.5.1 525 | */ 526 | private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) { 527 | encode3to4(threeBytes, 0, numSigBytes, b4, 0, options); 528 | return b4; 529 | } // end encode3to4 530 | 531 | /** 532 | *533 | * Encodes up to three bytes of the array source and writes the 534 | * resulting four Base64 bytes to destination. The source and 535 | * destination arrays can be manipulated anywhere along their length by 536 | * specifying srcOffset and destOffset. This method 537 | * does not check to make sure your arrays are large enough to accomodate 538 | * srcOffset + 3 for the source array or 539 | * destOffset + 4 for the destination array. The 540 | * actual number of significant bytes in your array is given by 541 | * numSigBytes. 542 | *
543 | *544 | * This is the lowest level of the encoding methods with all possible 545 | * parameters. 546 | *
547 | * 548 | * @param source 549 | * the array to convert 550 | * @param srcOffset 551 | * the index where conversion begins 552 | * @param numSigBytes 553 | * the number of significant bytes in your array 554 | * @param destination 555 | * the array to hold the conversion 556 | * @param destOffset 557 | * the index where output will be put 558 | * @return the destination array 559 | * @since 1.3 560 | */ 561 | private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset, 562 | int options) { 563 | 564 | byte[] ALPHABET = getAlphabet(options); 565 | 566 | // 1 2 3 567 | // 01234567890123456789012345678901 Bit position 568 | // --------000000001111111122222222 Array position from threeBytes 569 | // --------| || || || | Six bit groups to index ALPHABET 570 | // >>18 >>12 >> 6 >> 0 Right shift necessary 571 | // 0x3f 0x3f 0x3f Additional AND 572 | 573 | // Create buffer with zero-padding if there are only one or two 574 | // significant bytes passed in the array. 575 | // We have to shift left 24 in order to flush out the 1's that appear 576 | // when Java treats a value as negative that is cast from a byte to an 577 | // int. 578 | int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) 579 | | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) 580 | | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); 581 | 582 | switch (numSigBytes) { 583 | case 3: 584 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 585 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 586 | destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; 587 | destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; 588 | return destination; 589 | 590 | case 2: 591 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 592 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 593 | destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; 594 | destination[destOffset + 3] = EQUALS_SIGN; 595 | return destination; 596 | 597 | case 1: 598 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 599 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 600 | destination[destOffset + 2] = EQUALS_SIGN; 601 | destination[destOffset + 3] = EQUALS_SIGN; 602 | return destination; 603 | 604 | default: 605 | return destination; 606 | } // end switch 607 | } // end encode3to4 608 | 609 | /** 610 | * Performs Base64 encoding on theraw
ByteBuffer, writing it
611 | * to the encoded
ByteBuffer. This is an experimental feature.
612 | * Currently it does not pass along any options (such as
613 | * {@link #DO_BREAK_LINES} or {@link #GZIP}.
614 | *
615 | * @param raw
616 | * input buffer
617 | * @param encoded
618 | * output buffer
619 | * @since 2.3
620 | */
621 | public static void encode(java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded) {
622 | byte[] raw3 = new byte[3];
623 | byte[] enc4 = new byte[4];
624 |
625 | while (raw.hasRemaining()) {
626 | int rem = Math.min(3, raw.remaining());
627 | raw.get(raw3, 0, rem);
628 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
629 | encoded.put(enc4);
630 | } // end input remaining
631 | }
632 |
633 | /**
634 | * Performs Base64 encoding on the raw
ByteBuffer, writing it
635 | * to the encoded
CharBuffer. This is an experimental feature.
636 | * Currently it does not pass along any options (such as
637 | * {@link #DO_BREAK_LINES} or {@link #GZIP}.
638 | *
639 | * @param raw
640 | * input buffer
641 | * @param encoded
642 | * output buffer
643 | * @since 2.3
644 | */
645 | public static void encode(java.nio.ByteBuffer raw, java.nio.CharBuffer encoded) {
646 | byte[] raw3 = new byte[3];
647 | byte[] enc4 = new byte[4];
648 |
649 | while (raw.hasRemaining()) {
650 | int rem = Math.min(3, raw.remaining());
651 | raw.get(raw3, 0, rem);
652 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
653 | for (int i = 0; i < 4; i++) {
654 | encoded.put((char) (enc4[i] & 0xFF));
655 | }
656 | } // end input remaining
657 | }
658 |
659 | /**
660 | * Serializes an object and returns the Base64-encoded version of that
661 | * serialized object.
662 | *
663 | * 664 | * As of v 2.3, if the object cannot be serialized or there is another 665 | * error, the method will throw an java.io.IOException. This is new to 666 | * v2.3! In earlier versions, it just returned a null value, but in 667 | * retrospect that's a pretty poor way to handle it. 668 | *
669 | * 670 | * The object is not GZip-compressed before being encoded. 671 | * 672 | * @param serializableObject 673 | * The object to encode 674 | * @return The Base64-encoded object 675 | * @throws java.io.IOException 676 | * if there is an error 677 | * @throws NullPointerException 678 | * if serializedObject is null 679 | * @since 1.4 680 | */ 681 | public static String encodeObject(java.io.Serializable serializableObject) throws java.io.IOException { 682 | return encodeObject(serializableObject, NO_OPTIONS); 683 | } // end encodeObject 684 | 685 | /** 686 | * Serializes an object and returns the Base64-encoded version of that 687 | * serialized object. 688 | * 689 | *690 | * As of v 2.3, if the object cannot be serialized or there is another 691 | * error, the method will throw an java.io.IOException. This is new to 692 | * v2.3! In earlier versions, it just returned a null value, but in 693 | * retrospect that's a pretty poor way to handle it. 694 | *
695 | * 696 | * The object is not GZip-compressed before being encoded. 697 | *698 | * Example options: 699 | * 700 | *
701 | * GZIP: gzip-compresses object before encoding it. 702 | * DO_BREAK_LINES: break lines at 76 characters 703 | *704 | *
705 | * Example: encodeObject( myObj, Base64.GZIP )
or
706 | *
707 | * Example:
708 | * encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )
709 | *
710 | * @param serializableObject
711 | * The object to encode
712 | * @param options
713 | * Specified options
714 | * @return The Base64-encoded object
715 | * @see Base64#GZIP
716 | * @see Base64#DO_BREAK_LINES
717 | * @throws java.io.IOException
718 | * if there is an error
719 | * @since 2.0
720 | */
721 | public static String encodeObject(java.io.Serializable serializableObject, int options) throws java.io.IOException {
722 |
723 | if (serializableObject == null) {
724 | throw new NullPointerException("Cannot serialize a null object.");
725 | } // end if: null
726 |
727 | // Streams
728 | java.io.ByteArrayOutputStream baos = null;
729 | java.io.OutputStream b64os = null;
730 | java.util.zip.GZIPOutputStream gzos = null;
731 | java.io.ObjectOutputStream oos = null;
732 |
733 | try {
734 | // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
735 | baos = new java.io.ByteArrayOutputStream();
736 | b64os = new Base64.OutputStream(baos, ENCODE | options);
737 | if ((options & GZIP) != 0) {
738 | // Gzip
739 | gzos = new java.util.zip.GZIPOutputStream(b64os);
740 | oos = new java.io.ObjectOutputStream(gzos);
741 | } else {
742 | // Not gzipped
743 | oos = new java.io.ObjectOutputStream(b64os);
744 | }
745 | oos.writeObject(serializableObject);
746 | } // end try
747 | catch (java.io.IOException e) {
748 | // Catch it and then throw it immediately so that
749 | // the finally{} block is called for cleanup.
750 | throw e;
751 | } // end catch
752 | finally {
753 | try {
754 | oos.close();
755 | } catch (Exception e) {
756 | }
757 | try {
758 | gzos.close();
759 | } catch (Exception e) {
760 | }
761 | try {
762 | b64os.close();
763 | } catch (Exception e) {
764 | }
765 | try {
766 | baos.close();
767 | } catch (Exception e) {
768 | }
769 | } // end finally
770 |
771 | // Return value according to relevant encoding.
772 | try {
773 | return new String(baos.toByteArray(), PREFERRED_ENCODING);
774 | } // end try
775 | catch (java.io.UnsupportedEncodingException uue) {
776 | // Fall back to some Java default
777 | return new String(baos.toByteArray());
778 | } // end catch
779 |
780 | } // end encode
781 |
782 | /**
783 | * Encodes a byte array into Base64 notation. Does not GZip-compress data.
784 | *
785 | * @param source
786 | * The data to convert
787 | * @return The data in Base64-encoded form
788 | * @throws NullPointerException
789 | * if source array is null
790 | * @since 1.4
791 | */
792 | public static String encodeBytes(byte[] source) {
793 | // Since we're not going to have the GZIP encoding turned on,
794 | // we're not going to have an java.io.IOException thrown, so
795 | // we should not force the user to have to catch it.
796 | String encoded = null;
797 | try {
798 | encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
799 | } catch (java.io.IOException ex) {
800 | assert false : ex.getMessage();
801 | } // end catch
802 | assert encoded != null;
803 | return encoded;
804 | } // end encodeBytes
805 |
806 | /**
807 | * Encodes a byte array into Base64 notation.
808 | *
809 | * Example options: 810 | * 811 | *
812 | * GZIP: gzip-compresses object before encoding it. 813 | * DO_BREAK_LINES: break lines at 76 characters 814 | * Note: Technically, this makes your encoding non-compliant. 815 | *816 | *
817 | * Example: encodeBytes( myData, Base64.GZIP )
or
818 | *
819 | * Example:
820 | * encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
821 | *
822 | *
823 | *
824 | * As of v 2.3, if there is an error with the GZIP stream, the method will 825 | * throw an java.io.IOException. This is new to v2.3! In earlier 826 | * versions, it just returned a null value, but in retrospect that's a 827 | * pretty poor way to handle it. 828 | *
829 | * 830 | * 831 | * @param source 832 | * The data to convert 833 | * @param options 834 | * Specified options 835 | * @return The Base64-encoded data as a String 836 | * @see Base64#GZIP 837 | * @see Base64#DO_BREAK_LINES 838 | * @throws java.io.IOException 839 | * if there is an error 840 | * @throws NullPointerException 841 | * if source array is null 842 | * @since 2.0 843 | */ 844 | public static String encodeBytes(byte[] source, int options) throws java.io.IOException { 845 | return encodeBytes(source, 0, source.length, options); 846 | } // end encodeBytes 847 | 848 | /** 849 | * Encodes a byte array into Base64 notation. Does not GZip-compress data. 850 | * 851 | *852 | * As of v 2.3, if there is an error, the method will throw an 853 | * java.io.IOException. This is new to v2.3! In earlier versions, it 854 | * just returned a null value, but in retrospect that's a pretty poor way to 855 | * handle it. 856 | *
857 | * 858 | * 859 | * @param source 860 | * The data to convert 861 | * @param off 862 | * Offset in array where conversion should begin 863 | * @param len 864 | * Length of data to convert 865 | * @return The Base64-encoded data as a String 866 | * @throws NullPointerException 867 | * if source array is null 868 | * @throws IllegalArgumentException 869 | * if source array, offset, or length are invalid 870 | * @since 1.4 871 | */ 872 | public static String encodeBytes(byte[] source, int off, int len) { 873 | // Since we're not going to have the GZIP encoding turned on, 874 | // we're not going to have an java.io.IOException thrown, so 875 | // we should not force the user to have to catch it. 876 | String encoded = null; 877 | try { 878 | encoded = encodeBytes(source, off, len, NO_OPTIONS); 879 | } catch (java.io.IOException ex) { 880 | assert false : ex.getMessage(); 881 | } // end catch 882 | assert encoded != null; 883 | return encoded; 884 | } // end encodeBytes 885 | 886 | /** 887 | * Encodes a byte array into Base64 notation. 888 | *889 | * Example options: 890 | * 891 | *
892 | * GZIP: gzip-compresses object before encoding it. 893 | * DO_BREAK_LINES: break lines at 76 characters 894 | * Note: Technically, this makes your encoding non-compliant. 895 | *896 | *
897 | * Example: encodeBytes( myData, Base64.GZIP )
or
898 | *
899 | * Example:
900 | * encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
901 | *
902 | *
903 | *
904 | * As of v 2.3, if there is an error with the GZIP stream, the method will 905 | * throw an java.io.IOException. This is new to v2.3! In earlier 906 | * versions, it just returned a null value, but in retrospect that's a 907 | * pretty poor way to handle it. 908 | *
909 | * 910 | * 911 | * @param source 912 | * The data to convert 913 | * @param off 914 | * Offset in array where conversion should begin 915 | * @param len 916 | * Length of data to convert 917 | * @param options 918 | * Specified options 919 | * @return The Base64-encoded data as a String 920 | * @see Base64#GZIP 921 | * @see Base64#DO_BREAK_LINES 922 | * @throws java.io.IOException 923 | * if there is an error 924 | * @throws NullPointerException 925 | * if source array is null 926 | * @throws IllegalArgumentException 927 | * if source array, offset, or length are invalid 928 | * @since 2.0 929 | */ 930 | public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException { 931 | byte[] encoded = encodeBytesToBytes(source, off, len, options); 932 | 933 | // Return value according to relevant encoding. 934 | try { 935 | return new String(encoded, PREFERRED_ENCODING); 936 | } // end try 937 | catch (java.io.UnsupportedEncodingException uue) { 938 | return new String(encoded); 939 | } // end catch 940 | 941 | } // end encodeBytes 942 | 943 | /** 944 | * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead 945 | * of instantiating a String. This is more efficient if you're working with 946 | * I/O streams and have large data sets to encode. 947 | * 948 | * 949 | * @param source 950 | * The data to convert 951 | * @return The Base64-encoded data as a byte[] (of ASCII characters) 952 | * @throws NullPointerException 953 | * if source array is null 954 | * @since 2.3.1 955 | */ 956 | public static byte[] encodeBytesToBytes(byte[] source) { 957 | byte[] encoded = null; 958 | try { 959 | encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS); 960 | } catch (java.io.IOException ex) { 961 | assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 962 | } 963 | return encoded; 964 | } 965 | 966 | /** 967 | * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte 968 | * array instead of instantiating a String. This is more efficient if you're 969 | * working with I/O streams and have large data sets to encode. 970 | * 971 | * 972 | * @param source 973 | * The data to convert 974 | * @param off 975 | * Offset in array where conversion should begin 976 | * @param len 977 | * Length of data to convert 978 | * @param options 979 | * Specified options 980 | * @return The Base64-encoded data as a String 981 | * @see Base64#GZIP 982 | * @see Base64#DO_BREAK_LINES 983 | * @throws java.io.IOException 984 | * if there is an error 985 | * @throws NullPointerException 986 | * if source array is null 987 | * @throws IllegalArgumentException 988 | * if source array, offset, or length are invalid 989 | * @since 2.3.1 990 | */ 991 | public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException { 992 | 993 | if (source == null) { 994 | throw new NullPointerException("Cannot serialize a null array."); 995 | } // end if: null 996 | 997 | if (off < 0) { 998 | throw new IllegalArgumentException("Cannot have negative offset: " + off); 999 | } // end if: off < 0 1000 | 1001 | if (len < 0) { 1002 | throw new IllegalArgumentException("Cannot have length offset: " + len); 1003 | } // end if: len < 0 1004 | 1005 | if (off + len > source.length) { 1006 | throw new IllegalArgumentException(String.format( 1007 | "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); 1008 | } // end if: off < 0 1009 | 1010 | // Compress? 1011 | if ((options & GZIP) != 0) { 1012 | java.io.ByteArrayOutputStream baos = null; 1013 | java.util.zip.GZIPOutputStream gzos = null; 1014 | Base64.OutputStream b64os = null; 1015 | 1016 | try { 1017 | // GZip -> Base64 -> ByteArray 1018 | baos = new java.io.ByteArrayOutputStream(); 1019 | b64os = new Base64.OutputStream(baos, ENCODE | options); 1020 | gzos = new java.util.zip.GZIPOutputStream(b64os); 1021 | 1022 | gzos.write(source, off, len); 1023 | gzos.close(); 1024 | } // end try 1025 | catch (java.io.IOException e) { 1026 | // Catch it and then throw it immediately so that 1027 | // the finally{} block is called for cleanup. 1028 | throw e; 1029 | } // end catch 1030 | finally { 1031 | try { 1032 | gzos.close(); 1033 | } catch (Exception e) { 1034 | } 1035 | try { 1036 | b64os.close(); 1037 | } catch (Exception e) { 1038 | } 1039 | try { 1040 | baos.close(); 1041 | } catch (Exception e) { 1042 | } 1043 | } // end finally 1044 | 1045 | return baos.toByteArray(); 1046 | } // end if: compress 1047 | 1048 | // Else, don't compress. Better not to use streams at all then. 1049 | else { 1050 | boolean breakLines = (options & DO_BREAK_LINES) != 0; 1051 | 1052 | // int len43 = len * 4 / 3; 1053 | // byte[] outBuff = new byte[ ( len43 ) // Main 4:3 1054 | // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding 1055 | // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines 1056 | // Try to determine more precisely how big the array needs to be. 1057 | // If we get it right, we don't have to do an array copy, and 1058 | // we save a bunch of memory. 1059 | int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes 1060 | // needed 1061 | // for 1062 | // actual 1063 | // encoding 1064 | if (breakLines) { 1065 | encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline 1066 | // characters 1067 | } 1068 | byte[] outBuff = new byte[encLen]; 1069 | 1070 | int d = 0; 1071 | int e = 0; 1072 | int len2 = len - 2; 1073 | int lineLength = 0; 1074 | for (; d < len2; d += 3, e += 4) { 1075 | encode3to4(source, d + off, 3, outBuff, e, options); 1076 | 1077 | lineLength += 4; 1078 | if (breakLines && lineLength >= MAX_LINE_LENGTH) { 1079 | outBuff[e + 4] = NEW_LINE; 1080 | e++; 1081 | lineLength = 0; 1082 | } // end if: end of line 1083 | } // en dfor: each piece of array 1084 | 1085 | if (d < len) { 1086 | encode3to4(source, d + off, len - d, outBuff, e, options); 1087 | e += 4; 1088 | } // end if: some padding needed 1089 | 1090 | // Only resize array if we didn't guess it right. 1091 | if (e <= outBuff.length - 1) { 1092 | // If breaking lines and the last byte falls right at 1093 | // the line length (76 bytes per line), there will be 1094 | // one extra byte, and the array will need to be resized. 1095 | // Not too bad of an estimate on array size, I'd say. 1096 | byte[] finalOut = new byte[e]; 1097 | System.arraycopy(outBuff, 0, finalOut, 0, e); 1098 | // System.err.println("Having to resize array from " + 1099 | // outBuff.length + " to " + e ); 1100 | return finalOut; 1101 | } else { 1102 | // System.err.println("No need to resize array."); 1103 | return outBuff; 1104 | } 1105 | 1106 | } // end else: don't compress 1107 | 1108 | } // end encodeBytesToBytes 1109 | 1110 | /* ******** D E C O D I N G M E T H O D S ******** */ 1111 | 1112 | /** 1113 | * Decodes four bytes from array source and writes the resulting 1114 | * bytes (up to three of them) to destination. The source and 1115 | * destination arrays can be manipulated anywhere along their length by 1116 | * specifying srcOffset and destOffset. This method 1117 | * does not check to make sure your arrays are large enough to accomodate 1118 | * srcOffset + 4 for the source array or 1119 | * destOffset + 3 for the destination array. This 1120 | * method returns the actual number of bytes that were converted from the 1121 | * Base64 encoding. 1122 | *1123 | * This is the lowest level of the decoding methods with all possible 1124 | * parameters. 1125 | *
1126 | * 1127 | * 1128 | * @param source 1129 | * the array to convert 1130 | * @param srcOffset 1131 | * the index where conversion begins 1132 | * @param destination 1133 | * the array to hold the conversion 1134 | * @param destOffset 1135 | * the index where output will be put 1136 | * @param options 1137 | * alphabet type is pulled from this (standard, url-safe, 1138 | * ordered) 1139 | * @return the number of decoded bytes converted 1140 | * @throws NullPointerException 1141 | * if source or destination arrays are null 1142 | * @throws IllegalArgumentException 1143 | * if srcOffset or destOffset are invalid or there is not enough 1144 | * room in the array. 1145 | * @since 1.3 1146 | */ 1147 | private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, int options) { 1148 | 1149 | // Lots of error checking and exception throwing 1150 | if (source == null) { 1151 | throw new NullPointerException("Source array was null."); 1152 | } // end if 1153 | if (destination == null) { 1154 | throw new NullPointerException("Destination array was null."); 1155 | } // end if 1156 | if (srcOffset < 0 || srcOffset + 3 >= source.length) { 1157 | throw new IllegalArgumentException(String.format( 1158 | "Source array with length %d cannot have offset of %d and still process four bytes.", 1159 | source.length, srcOffset)); 1160 | } // end if 1161 | if (destOffset < 0 || destOffset + 2 >= destination.length) { 1162 | throw new IllegalArgumentException(String.format( 1163 | "Destination array with length %d cannot have offset of %d and still store three bytes.", 1164 | destination.length, destOffset)); 1165 | } // end if 1166 | 1167 | byte[] DECODABET = getDecodabet(options); 1168 | 1169 | // Example: Dk== 1170 | if (source[srcOffset + 2] == EQUALS_SIGN) { 1171 | // Two ways to do the same thing. Don't know which way I like best. 1172 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 1173 | // ) 1174 | // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 1175 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 1176 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); 1177 | 1178 | destination[destOffset] = (byte) (outBuff >>> 16); 1179 | return 1; 1180 | } 1181 | 1182 | // Example: DkL= 1183 | else if (source[srcOffset + 3] == EQUALS_SIGN) { 1184 | // Two ways to do the same thing. Don't know which way I like best. 1185 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 1186 | // ) 1187 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1188 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 1189 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 1190 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) 1191 | | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); 1192 | 1193 | destination[destOffset] = (byte) (outBuff >>> 16); 1194 | destination[destOffset + 1] = (byte) (outBuff >>> 8); 1195 | return 2; 1196 | } 1197 | 1198 | // Example: DkLE 1199 | else { 1200 | // Two ways to do the same thing. Don't know which way I like best. 1201 | // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 1202 | // ) 1203 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1204 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 1205 | // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 1206 | int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) 1207 | | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) 1208 | | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF)); 1209 | 1210 | destination[destOffset] = (byte) (outBuff >> 16); 1211 | destination[destOffset + 1] = (byte) (outBuff >> 8); 1212 | destination[destOffset + 2] = (byte) (outBuff); 1213 | 1214 | return 3; 1215 | } 1216 | } // end decodeToBytes 1217 | 1218 | /** 1219 | * Low-level access to decoding ASCII characters in the form of a byte 1220 | * array. Ignores GUNZIP option, if it's set. This is not 1221 | * generally a recommended method, although it is used internally as part of 1222 | * the decoding process. Special case: if len = 0, an empty array is 1223 | * returned. Still, if you need more speed and reduced memory footprint (and 1224 | * aren't gzipping), consider this method. 1225 | * 1226 | * @param source 1227 | * The Base64 encoded data 1228 | * @return decoded data 1229 | * @since 2.3.1 1230 | */ 1231 | public static byte[] decode(byte[] source) throws java.io.IOException { 1232 | byte[] decoded = null; 1233 | // try { 1234 | decoded = decode(source, 0, source.length, Base64.NO_OPTIONS); 1235 | // } catch( java.io.IOException ex ) { 1236 | // assert false : 1237 | // "IOExceptions only come from GZipping, which is turned off: " + 1238 | // ex.getMessage(); 1239 | // } 1240 | return decoded; 1241 | } 1242 | 1243 | /** 1244 | * Low-level access to decoding ASCII characters in the form of a byte 1245 | * array. Ignores GUNZIP option, if it's set. This is not 1246 | * generally a recommended method, although it is used internally as part of 1247 | * the decoding process. Special case: if len = 0, an empty array is 1248 | * returned. Still, if you need more speed and reduced memory footprint (and 1249 | * aren't gzipping), consider this method. 1250 | * 1251 | * @param source 1252 | * The Base64 encoded data 1253 | * @param off 1254 | * The offset of where to begin decoding 1255 | * @param len 1256 | * The length of characters to decode 1257 | * @param options 1258 | * Can specify options such as alphabet type to use 1259 | * @return decoded data 1260 | * @throws java.io.IOException 1261 | * If bogus characters exist in source data 1262 | * @since 1.3 1263 | */ 1264 | public static byte[] decode(byte[] source, int off, int len, int options) throws java.io.IOException { 1265 | 1266 | // Lots of error checking and exception throwing 1267 | if (source == null) { 1268 | throw new NullPointerException("Cannot decode null source array."); 1269 | } // end if 1270 | if (off < 0 || off + len > source.length) { 1271 | throw new IllegalArgumentException(String.format( 1272 | "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, 1273 | len)); 1274 | } // end if 1275 | 1276 | if (len == 0) { 1277 | return new byte[0]; 1278 | } else if (len < 4) { 1279 | throw new IllegalArgumentException( 1280 | "Base64-encoded string must have at least four characters, but length specified was " + len); 1281 | } // end if 1282 | 1283 | byte[] DECODABET = getDecodabet(options); 1284 | 1285 | int len34 = len * 3 / 4; // Estimate on array size 1286 | byte[] outBuff = new byte[len34]; // Upper limit on size of output 1287 | int outBuffPosn = 0; // Keep track of where we're writing 1288 | 1289 | byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating 1290 | // white space 1291 | int b4Posn = 0; // Keep track of four byte input buffer 1292 | int i = 0; // Source array counter 1293 | byte sbiDecode = 0; // Special value from DECODABET 1294 | 1295 | for (i = off; i < off + len; i++) { // Loop through source 1296 | 1297 | sbiDecode = DECODABET[source[i] & 0xFF]; 1298 | 1299 | // White space, Equals sign, or legit Base64 character 1300 | // Note the values such as -5 and -9 in the 1301 | // DECODABETs at the top of the file. 1302 | if (sbiDecode >= WHITE_SPACE_ENC) { 1303 | if (sbiDecode >= EQUALS_SIGN_ENC) { 1304 | b4[b4Posn++] = source[i]; // Save non-whitespace 1305 | if (b4Posn > 3) { // Time to decode? 1306 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options); 1307 | b4Posn = 0; 1308 | 1309 | // If that was the equals sign, break out of 'for' loop 1310 | if (source[i] == EQUALS_SIGN) { 1311 | break; 1312 | } // end if: equals sign 1313 | } // end if: quartet built 1314 | } // end if: equals sign or better 1315 | } // end if: white space, equals sign or better 1316 | else { 1317 | // There's a bad input character in the Base64 stream. 1318 | throw new java.io.IOException(String.format( 1319 | "Bad Base64 input character decimal %d in array position %d", source[i] & 0xFF, i)); 1320 | } // end else: 1321 | } // each input character 1322 | 1323 | byte[] out = new byte[outBuffPosn]; 1324 | System.arraycopy(outBuff, 0, out, 0, outBuffPosn); 1325 | return out; 1326 | } // end decode 1327 | 1328 | /** 1329 | * Decodes data from Base64 notation, automatically detecting 1330 | * gzip-compressed data and decompressing it. 1331 | * 1332 | * @param s 1333 | * the string to decode 1334 | * @return the decoded data 1335 | * @throws java.io.IOException 1336 | * If there is a problem 1337 | * @since 1.4 1338 | */ 1339 | public static byte[] decode(String s) throws java.io.IOException { 1340 | return decode(s, NO_OPTIONS); 1341 | } 1342 | 1343 | /** 1344 | * Decodes data from Base64 notation, automatically detecting 1345 | * gzip-compressed data and decompressing it. 1346 | * 1347 | * @param s 1348 | * the string to decode 1349 | * @param options 1350 | * encode options such as URL_SAFE 1351 | * @return the decoded data 1352 | * @throws java.io.IOException 1353 | * if there is an error 1354 | * @throws NullPointerException 1355 | * if s is null 1356 | * @since 1.4 1357 | */ 1358 | public static byte[] decode(String s, int options) throws java.io.IOException { 1359 | 1360 | if (s == null) { 1361 | throw new NullPointerException("Input string was null."); 1362 | } // end if 1363 | 1364 | byte[] bytes; 1365 | try { 1366 | bytes = s.getBytes(PREFERRED_ENCODING); 1367 | } // end try 1368 | catch (java.io.UnsupportedEncodingException uee) { 1369 | bytes = s.getBytes(); 1370 | } // end catch 1371 | // 1372 | 1373 | // Decode 1374 | bytes = decode(bytes, 0, bytes.length, options); 1375 | 1376 | // Check to see if it's gzip-compressed 1377 | // GZIP Magic Two-Byte Number: 0x8b1f (35615) 1378 | boolean dontGunzip = (options & DONT_GUNZIP) != 0; 1379 | if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) { 1380 | 1381 | int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); 1382 | if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { 1383 | java.io.ByteArrayInputStream bais = null; 1384 | java.util.zip.GZIPInputStream gzis = null; 1385 | java.io.ByteArrayOutputStream baos = null; 1386 | byte[] buffer = new byte[2048]; 1387 | int length = 0; 1388 | 1389 | try { 1390 | baos = new java.io.ByteArrayOutputStream(); 1391 | bais = new java.io.ByteArrayInputStream(bytes); 1392 | gzis = new java.util.zip.GZIPInputStream(bais); 1393 | 1394 | while ((length = gzis.read(buffer)) >= 0) { 1395 | baos.write(buffer, 0, length); 1396 | } // end while: reading input 1397 | 1398 | // No error? Get new bytes. 1399 | bytes = baos.toByteArray(); 1400 | 1401 | } // end try 1402 | catch (java.io.IOException e) { 1403 | e.printStackTrace(); 1404 | // Just return originally-decoded bytes 1405 | } // end catch 1406 | finally { 1407 | try { 1408 | baos.close(); 1409 | } catch (Exception e) { 1410 | } 1411 | try { 1412 | gzis.close(); 1413 | } catch (Exception e) { 1414 | } 1415 | try { 1416 | bais.close(); 1417 | } catch (Exception e) { 1418 | } 1419 | } // end finally 1420 | 1421 | } // end if: gzipped 1422 | } // end if: bytes.length >= 2 1423 | 1424 | return bytes; 1425 | } // end decode 1426 | 1427 | /** 1428 | * Attempts to decode Base64 data and deserialize a Java Object within. 1429 | * Returns null if there was an error. 1430 | * 1431 | * @param encodedObject 1432 | * The Base64 data to decode 1433 | * @return The decoded and deserialized object 1434 | * @throws NullPointerException 1435 | * if encodedObject is null 1436 | * @throws java.io.IOException 1437 | * if there is a general error 1438 | * @throws ClassNotFoundException 1439 | * if the decoded object is of a class that cannot be found by 1440 | * the JVM 1441 | * @since 1.5 1442 | */ 1443 | public static Object decodeToObject(String encodedObject) throws java.io.IOException, 1444 | java.lang.ClassNotFoundException { 1445 | return decodeToObject(encodedObject, NO_OPTIONS, null); 1446 | } 1447 | 1448 | /** 1449 | * Attempts to decode Base64 data and deserialize a Java Object within. 1450 | * Returns null if there was an error. If loader is not 1451 | * null, it will be the class loader used when deserializing. 1452 | * 1453 | * @param encodedObject 1454 | * The Base64 data to decode 1455 | * @param options 1456 | * Various parameters related to decoding 1457 | * @param loader 1458 | * Optional class loader to use in deserializing classes. 1459 | * @return The decoded and deserialized object 1460 | * @throws NullPointerException 1461 | * if encodedObject is null 1462 | * @throws java.io.IOException 1463 | * if there is a general error 1464 | * @throws ClassNotFoundException 1465 | * if the decoded object is of a class that cannot be found by 1466 | * the JVM 1467 | * @since 2.3.4 1468 | */ 1469 | public static Object decodeToObject(String encodedObject, int options, final ClassLoader loader) 1470 | throws java.io.IOException, java.lang.ClassNotFoundException { 1471 | 1472 | // Decode and gunzip if necessary 1473 | byte[] objBytes = decode(encodedObject, options); 1474 | 1475 | java.io.ByteArrayInputStream bais = null; 1476 | java.io.ObjectInputStream ois = null; 1477 | Object obj = null; 1478 | 1479 | try { 1480 | bais = new java.io.ByteArrayInputStream(objBytes); 1481 | 1482 | // If no custom class loader is provided, use Java's builtin OIS. 1483 | if (loader == null) { 1484 | ois = new java.io.ObjectInputStream(bais); 1485 | } // end if: no loader provided 1486 | 1487 | // Else make a customized object input stream that uses 1488 | // the provided class loader. 1489 | else { 1490 | ois = new java.io.ObjectInputStream(bais) { 1491 | @Override 1492 | public Class> resolveClass(java.io.ObjectStreamClass streamClass) throws java.io.IOException, 1493 | ClassNotFoundException { 1494 | Class c = Class.forName(streamClass.getName(), false, loader); 1495 | if (c == null) { 1496 | return super.resolveClass(streamClass); 1497 | } else { 1498 | return c; // Class loader knows of this class. 1499 | } // end else: not null 1500 | } // end resolveClass 1501 | }; // end ois 1502 | } // end else: no custom class loader 1503 | 1504 | obj = ois.readObject(); 1505 | } // end try 1506 | catch (java.io.IOException e) { 1507 | throw e; // Catch and throw in order to execute finally{} 1508 | } // end catch 1509 | catch (java.lang.ClassNotFoundException e) { 1510 | throw e; // Catch and throw in order to execute finally{} 1511 | } // end catch 1512 | finally { 1513 | try { 1514 | bais.close(); 1515 | } catch (Exception e) { 1516 | } 1517 | try { 1518 | ois.close(); 1519 | } catch (Exception e) { 1520 | } 1521 | } // end finally 1522 | 1523 | return obj; 1524 | } // end decodeObject 1525 | 1526 | /** 1527 | * Convenience method for encoding data to a file. 1528 | * 1529 | *1530 | * As of v 2.3, if there is a error, the method will throw an 1531 | * java.io.IOException. This is new to v2.3! In earlier versions, it 1532 | * just returned false, but in retrospect that's a pretty poor way to handle 1533 | * it. 1534 | *
1535 | * 1536 | * @param dataToEncode 1537 | * byte array of data to encode in base64 form 1538 | * @param filename 1539 | * Filename for saving encoded data 1540 | * @throws java.io.IOException 1541 | * if there is an error 1542 | * @throws NullPointerException 1543 | * if dataToEncode is null 1544 | * @since 2.1 1545 | */ 1546 | public static void encodeToFile(byte[] dataToEncode, String filename) throws java.io.IOException { 1547 | 1548 | if (dataToEncode == null) { 1549 | throw new NullPointerException("Data to encode was null."); 1550 | } // end iff 1551 | 1552 | Base64.OutputStream bos = null; 1553 | try { 1554 | bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE); 1555 | bos.write(dataToEncode); 1556 | } // end try 1557 | catch (java.io.IOException e) { 1558 | throw e; // Catch and throw to execute finally{} block 1559 | } // end catch: java.io.IOException 1560 | finally { 1561 | try { 1562 | bos.close(); 1563 | } catch (Exception e) { 1564 | } 1565 | } // end finally 1566 | 1567 | } // end encodeToFile 1568 | 1569 | /** 1570 | * Convenience method for decoding data to a file. 1571 | * 1572 | *1573 | * As of v 2.3, if there is a error, the method will throw an 1574 | * java.io.IOException. This is new to v2.3! In earlier versions, it 1575 | * just returned false, but in retrospect that's a pretty poor way to handle 1576 | * it. 1577 | *
1578 | * 1579 | * @param dataToDecode 1580 | * Base64-encoded data as a string 1581 | * @param filename 1582 | * Filename for saving decoded data 1583 | * @throws java.io.IOException 1584 | * if there is an error 1585 | * @since 2.1 1586 | */ 1587 | public static void decodeToFile(String dataToDecode, String filename) throws java.io.IOException { 1588 | 1589 | Base64.OutputStream bos = null; 1590 | try { 1591 | bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE); 1592 | bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); 1593 | } // end try 1594 | catch (java.io.IOException e) { 1595 | throw e; // Catch and throw to execute finally{} block 1596 | } // end catch: java.io.IOException 1597 | finally { 1598 | try { 1599 | bos.close(); 1600 | } catch (Exception e) { 1601 | } 1602 | } // end finally 1603 | 1604 | } // end decodeToFile 1605 | 1606 | /** 1607 | * Convenience method for reading a base64-encoded file and decoding it. 1608 | * 1609 | *1610 | * As of v 2.3, if there is a error, the method will throw an 1611 | * java.io.IOException. This is new to v2.3! In earlier versions, it 1612 | * just returned false, but in retrospect that's a pretty poor way to handle 1613 | * it. 1614 | *
1615 | * 1616 | * @param filename 1617 | * Filename for reading encoded data 1618 | * @return decoded byte array 1619 | * @throws java.io.IOException 1620 | * if there is an error 1621 | * @since 2.1 1622 | */ 1623 | public static byte[] decodeFromFile(String filename) throws java.io.IOException { 1624 | 1625 | byte[] decodedData = null; 1626 | Base64.InputStream bis = null; 1627 | try { 1628 | // Set up some useful variables 1629 | java.io.File file = new java.io.File(filename); 1630 | byte[] buffer = null; 1631 | int length = 0; 1632 | int numBytes = 0; 1633 | 1634 | // Check for size of file 1635 | if (file.length() > Integer.MAX_VALUE) { 1636 | throw new java.io.IOException("File is too big for this convenience method (" + file.length() 1637 | + " bytes)."); 1638 | } // end if: file too big for int index 1639 | buffer = new byte[(int) file.length()]; 1640 | 1641 | // Open a stream 1642 | bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), 1643 | Base64.DECODE); 1644 | 1645 | // Read until done 1646 | while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { 1647 | length += numBytes; 1648 | } // end while 1649 | 1650 | // Save in a variable to return 1651 | decodedData = new byte[length]; 1652 | System.arraycopy(buffer, 0, decodedData, 0, length); 1653 | 1654 | } // end try 1655 | catch (java.io.IOException e) { 1656 | throw e; // Catch and release to execute finally{} 1657 | } // end catch: java.io.IOException 1658 | finally { 1659 | try { 1660 | bis.close(); 1661 | } catch (Exception e) { 1662 | } 1663 | } // end finally 1664 | 1665 | return decodedData; 1666 | } // end decodeFromFile 1667 | 1668 | /** 1669 | * Convenience method for reading a binary file and base64-encoding it. 1670 | * 1671 | *1672 | * As of v 2.3, if there is a error, the method will throw an 1673 | * java.io.IOException. This is new to v2.3! In earlier versions, it 1674 | * just returned false, but in retrospect that's a pretty poor way to handle 1675 | * it. 1676 | *
1677 | * 1678 | * @param filename 1679 | * Filename for reading binary data 1680 | * @return base64-encoded string 1681 | * @throws java.io.IOException 1682 | * if there is an error 1683 | * @since 2.1 1684 | */ 1685 | public static String encodeFromFile(String filename) throws java.io.IOException { 1686 | 1687 | String encodedData = null; 1688 | Base64.InputStream bis = null; 1689 | try { 1690 | // Set up some useful variables 1691 | java.io.File file = new java.io.File(filename); 1692 | byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need 1693 | // max() 1694 | // for 1695 | // math 1696 | // on 1697 | // small 1698 | // files 1699 | // (v2.2.1); 1700 | // Need 1701 | // +1 1702 | // for 1703 | // a 1704 | // few 1705 | // corner 1706 | // cases 1707 | // (v2.3.5) 1708 | int length = 0; 1709 | int numBytes = 0; 1710 | 1711 | // Open a stream 1712 | bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), 1713 | Base64.ENCODE); 1714 | 1715 | // Read until done 1716 | while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { 1717 | length += numBytes; 1718 | } // end while 1719 | 1720 | // Save in a variable to return 1721 | encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING); 1722 | 1723 | } // end try 1724 | catch (java.io.IOException e) { 1725 | throw e; // Catch and release to execute finally{} 1726 | } // end catch: java.io.IOException 1727 | finally { 1728 | try { 1729 | bis.close(); 1730 | } catch (Exception e) { 1731 | } 1732 | } // end finally 1733 | 1734 | return encodedData; 1735 | } // end encodeFromFile 1736 | 1737 | /** 1738 | * Reads infile and encodes it to outfile. 1739 | * 1740 | * @param infile 1741 | * Input file 1742 | * @param outfile 1743 | * Output file 1744 | * @throws java.io.IOException 1745 | * if there is an error 1746 | * @since 2.2 1747 | */ 1748 | public static void encodeFileToFile(String infile, String outfile) throws java.io.IOException { 1749 | 1750 | String encoded = Base64.encodeFromFile(infile); 1751 | java.io.OutputStream out = null; 1752 | try { 1753 | out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile)); 1754 | out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit 1755 | // output. 1756 | } // end try 1757 | catch (java.io.IOException e) { 1758 | throw e; // Catch and release to execute finally{} 1759 | } // end catch 1760 | finally { 1761 | try { 1762 | out.close(); 1763 | } catch (Exception ex) { 1764 | } 1765 | } // end finally 1766 | } // end encodeFileToFile 1767 | 1768 | /** 1769 | * Reads infile and decodes it to outfile. 1770 | * 1771 | * @param infile 1772 | * Input file 1773 | * @param outfile 1774 | * Output file 1775 | * @throws java.io.IOException 1776 | * if there is an error 1777 | * @since 2.2 1778 | */ 1779 | public static void decodeFileToFile(String infile, String outfile) throws java.io.IOException { 1780 | 1781 | byte[] decoded = Base64.decodeFromFile(infile); 1782 | java.io.OutputStream out = null; 1783 | try { 1784 | out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile)); 1785 | out.write(decoded); 1786 | } // end try 1787 | catch (java.io.IOException e) { 1788 | throw e; // Catch and release to execute finally{} 1789 | } // end catch 1790 | finally { 1791 | try { 1792 | out.close(); 1793 | } catch (Exception ex) { 1794 | } 1795 | } // end finally 1796 | } // end decodeFileToFile 1797 | 1798 | /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 1799 | 1800 | /** 1801 | * A {@link Base64.InputStream} will read data from another 1802 | * java.io.InputStream, given in the constructor, and encode/decode 1803 | * to/from Base64 notation on the fly. 1804 | * 1805 | * @see Base64 1806 | * @since 1.3 1807 | */ 1808 | public static class InputStream extends java.io.FilterInputStream { 1809 | 1810 | private final boolean encode; // Encoding or decoding 1811 | private int position; // Current position in the buffer 1812 | private final byte[] buffer; // Small buffer holding converted data 1813 | private final int bufferLength; // Length of buffer (3 or 4) 1814 | private int numSigBytes; // Number of meaningful bytes in the buffer 1815 | private int lineLength; 1816 | private final boolean breakLines; // Break lines at less than 80 1817 | // characters 1818 | private final int options; // Record options used to create the stream. 1819 | private final byte[] decodabet; // Local copies to avoid extra method 1820 | // calls 1821 | 1822 | /** 1823 | * Constructs a {@link Base64.InputStream} in DECODE mode. 1824 | * 1825 | * @param in 1826 | * the java.io.InputStream from which to read data. 1827 | * @since 1.3 1828 | */ 1829 | public InputStream(java.io.InputStream in) { 1830 | this(in, DECODE); 1831 | } // end constructor 1832 | 1833 | /** 1834 | * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE 1835 | * mode. 1836 | *1837 | * Valid options: 1838 | * 1839 | *
1840 | * ENCODE or DECODE: Encode or Decode as data is read. 1841 | * DO_BREAK_LINES: break lines at 76 characters 1842 | * (only meaningful when encoding) 1843 | *1844 | *
1845 | * Example: new Base64.InputStream( in, Base64.DECODE )
1846 | *
1847 | *
1848 | * @param in
1849 | * the java.io.InputStream from which to read data.
1850 | * @param options
1851 | * Specified options
1852 | * @see Base64#ENCODE
1853 | * @see Base64#DECODE
1854 | * @see Base64#DO_BREAK_LINES
1855 | * @since 2.0
1856 | */
1857 | public InputStream(java.io.InputStream in, int options) {
1858 |
1859 | super(in);
1860 | this.options = options; // Record for later
1861 | this.breakLines = (options & DO_BREAK_LINES) > 0;
1862 | this.encode = (options & ENCODE) > 0;
1863 | this.bufferLength = encode ? 4 : 3;
1864 | this.buffer = new byte[bufferLength];
1865 | this.position = -1;
1866 | this.lineLength = 0;
1867 | this.decodabet = getDecodabet(options);
1868 | } // end constructor
1869 |
1870 | /**
1871 | * Reads enough of the input stream to convert to/from Base64 and
1872 | * returns the next byte.
1873 | *
1874 | * @return next byte
1875 | * @since 1.3
1876 | */
1877 | @Override
1878 | public int read() throws java.io.IOException {
1879 |
1880 | // Do we need to get data?
1881 | if (position < 0) {
1882 | if (encode) {
1883 | byte[] b3 = new byte[3];
1884 | int numBinaryBytes = 0;
1885 | for (int i = 0; i < 3; i++) {
1886 | int b = in.read();
1887 |
1888 | // If end of stream, b is -1.
1889 | if (b >= 0) {
1890 | b3[i] = (byte) b;
1891 | numBinaryBytes++;
1892 | } else {
1893 | break; // out of for loop
1894 | } // end else: end of stream
1895 |
1896 | } // end for: each needed input byte
1897 |
1898 | if (numBinaryBytes > 0) {
1899 | encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1900 | position = 0;
1901 | numSigBytes = 4;
1902 | } // end if: got data
1903 | else {
1904 | return -1; // Must be end of stream
1905 | } // end else
1906 | } // end if: encoding
1907 |
1908 | // Else decoding
1909 | else {
1910 | byte[] b4 = new byte[4];
1911 | int i = 0;
1912 | for (i = 0; i < 4; i++) {
1913 | // Read four "meaningful" bytes:
1914 | int b = 0;
1915 | do {
1916 | b = in.read();
1917 | } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1918 |
1919 | if (b < 0) {
1920 | break; // Reads a -1 if end of stream
1921 | } // end if: end of stream
1922 |
1923 | b4[i] = (byte) b;
1924 | } // end for: each needed input byte
1925 |
1926 | if (i == 4) {
1927 | numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1928 | position = 0;
1929 | } // end if: got four characters
1930 | else if (i == 0) {
1931 | return -1;
1932 | } // end else if: also padded correctly
1933 | else {
1934 | // Must have broken out from above.
1935 | throw new java.io.IOException("Improperly padded Base64 input.");
1936 | } // end
1937 |
1938 | } // end else: decode
1939 | } // end else: get data
1940 |
1941 | // Got data?
1942 | if (position >= 0) {
1943 | // End of relevant data?
1944 | if ( /* !encode && */position >= numSigBytes) {
1945 | return -1;
1946 | } // end if: got data
1947 |
1948 | if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1949 | lineLength = 0;
1950 | return '\n';
1951 | } // end if
1952 | else {
1953 | lineLength++; // This isn't important when decoding
1954 | // but throwing an extra "if" seems
1955 | // just as wasteful.
1956 |
1957 | int b = buffer[position++];
1958 |
1959 | if (position >= bufferLength) {
1960 | position = -1;
1961 | } // end if: end
1962 |
1963 | return b & 0xFF; // This is how you "cast" a byte that's
1964 | // intended to be unsigned.
1965 | } // end else
1966 | } // end if: position >= 0
1967 |
1968 | // Else error
1969 | else {
1970 | throw new java.io.IOException("Error in Base64 code reading stream.");
1971 | } // end else
1972 | } // end read
1973 |
1974 | /**
1975 | * Calls {@link #read()} repeatedly until the end of stream is reached
1976 | * or len bytes are read. Returns number of bytes read into
1977 | * array or -1 if end of stream is encountered.
1978 | *
1979 | * @param dest
1980 | * array to hold values
1981 | * @param off
1982 | * offset for array
1983 | * @param len
1984 | * max number of bytes to read into array
1985 | * @return bytes read into array or -1 if end of stream is encountered.
1986 | * @since 1.3
1987 | */
1988 | @Override
1989 | public int read(byte[] dest, int off, int len) throws java.io.IOException {
1990 | int i;
1991 | int b;
1992 | for (i = 0; i < len; i++) {
1993 | b = read();
1994 |
1995 | if (b >= 0) {
1996 | dest[off + i] = (byte) b;
1997 | } else if (i == 0) {
1998 | return -1;
1999 | } else {
2000 | break; // Out of 'for' loop
2001 | } // Out of 'for' loop
2002 | } // end for: each byte read
2003 | return i;
2004 | } // end read
2005 |
2006 | } // end inner class InputStream
2007 |
2008 | /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
2009 |
2010 | /**
2011 | * A {@link Base64.OutputStream} will write data to another
2012 | * java.io.OutputStream, given in the constructor, and
2013 | * encode/decode to/from Base64 notation on the fly.
2014 | *
2015 | * @see Base64
2016 | * @since 1.3
2017 | */
2018 | public static class OutputStream extends java.io.FilterOutputStream {
2019 |
2020 | private final boolean encode;
2021 | private int position;
2022 | private byte[] buffer;
2023 | private final int bufferLength;
2024 | private int lineLength;
2025 | private final boolean breakLines;
2026 | private final byte[] b4; // Scratch used in a few places
2027 | private boolean suspendEncoding;
2028 | private final int options; // Record for later
2029 | private final byte[] decodabet; // Local copies to avoid extra method
2030 | // calls
2031 |
2032 | /**
2033 | * Constructs a {@link Base64.OutputStream} in ENCODE mode.
2034 | *
2035 | * @param out
2036 | * the java.io.OutputStream to which data will be
2037 | * written.
2038 | * @since 1.3
2039 | */
2040 | public OutputStream(java.io.OutputStream out) {
2041 | this(out, ENCODE);
2042 | } // end constructor
2043 |
2044 | /**
2045 | * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
2046 | * mode.
2047 | *
2048 | * Valid options: 2049 | * 2050 | *
2051 | * ENCODE or DECODE: Encode or Decode as data is read. 2052 | * DO_BREAK_LINES: don't break lines at 76 characters 2053 | * (only meaningful when encoding) 2054 | *2055 | *
2056 | * Example: new Base64.OutputStream( out, Base64.ENCODE )
2057 | *
2058 | * @param out
2059 | * the java.io.OutputStream to which data will be
2060 | * written.
2061 | * @param options
2062 | * Specified options.
2063 | * @see Base64#ENCODE
2064 | * @see Base64#DECODE
2065 | * @see Base64#DO_BREAK_LINES
2066 | * @since 1.3
2067 | */
2068 | public OutputStream(java.io.OutputStream out, int options) {
2069 | super(out);
2070 | this.breakLines = (options & DO_BREAK_LINES) != 0;
2071 | this.encode = (options & ENCODE) != 0;
2072 | this.bufferLength = encode ? 3 : 4;
2073 | this.buffer = new byte[bufferLength];
2074 | this.position = 0;
2075 | this.lineLength = 0;
2076 | this.suspendEncoding = false;
2077 | this.b4 = new byte[4];
2078 | this.options = options;
2079 | this.decodabet = getDecodabet(options);
2080 | } // end constructor
2081 |
2082 | /**
2083 | * Writes the byte to the output stream after converting to/from Base64
2084 | * notation. When encoding, bytes are buffered three at a time before
2085 | * the output stream actually gets a write() call. When decoding, bytes
2086 | * are buffered four at a time.
2087 | *
2088 | * @param theByte
2089 | * the byte to write
2090 | * @since 1.3
2091 | */
2092 | @Override
2093 | public void write(int theByte) throws java.io.IOException {
2094 | // Encoding suspended?
2095 | if (suspendEncoding) {
2096 | this.out.write(theByte);
2097 | return;
2098 | } // end if: supsended
2099 |
2100 | // Encode?
2101 | if (encode) {
2102 | buffer[position++] = (byte) theByte;
2103 | if (position >= bufferLength) { // Enough to encode.
2104 |
2105 | this.out.write(encode3to4(b4, buffer, bufferLength, options));
2106 |
2107 | lineLength += 4;
2108 | if (breakLines && lineLength >= MAX_LINE_LENGTH) {
2109 | this.out.write(NEW_LINE);
2110 | lineLength = 0;
2111 | } // end if: end of line
2112 |
2113 | position = 0;
2114 | } // end if: enough to output
2115 | } // end if: encoding
2116 |
2117 | // Else, Decoding
2118 | else {
2119 | // Meaningful Base64 character?
2120 | if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
2121 | buffer[position++] = (byte) theByte;
2122 | if (position >= bufferLength) { // Enough to output.
2123 |
2124 | int len = Base64.decode4to3(buffer, 0, b4, 0, options);
2125 | out.write(b4, 0, len);
2126 | position = 0;
2127 | } // end if: enough to output
2128 | } // end if: meaningful base64 character
2129 | else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
2130 | throw new java.io.IOException("Invalid character in Base64 data.");
2131 | } // end else: not white space either
2132 | } // end else: decoding
2133 | } // end write
2134 |
2135 | /**
2136 | * Calls {@link #write(int)} repeatedly until len bytes are
2137 | * written.
2138 | *
2139 | * @param theBytes
2140 | * array from which to read bytes
2141 | * @param off
2142 | * offset for array
2143 | * @param len
2144 | * max number of bytes to read into array
2145 | * @since 1.3
2146 | */
2147 | @Override
2148 | public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
2149 | // Encoding suspended?
2150 | if (suspendEncoding) {
2151 | this.out.write(theBytes, off, len);
2152 | return;
2153 | } // end if: supsended
2154 |
2155 | for (int i = 0; i < len; i++) {
2156 | write(theBytes[off + i]);
2157 | } // end for: each byte written
2158 |
2159 | } // end write
2160 |
2161 | /**
2162 | * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
2163 | * without closing the stream.
2164 | *
2165 | * @throws java.io.IOException
2166 | * if there's an error.
2167 | */
2168 | public void flushBase64() throws java.io.IOException {
2169 | if (position > 0) {
2170 | if (encode) {
2171 | out.write(encode3to4(b4, buffer, position, options));
2172 | position = 0;
2173 | } // end if: encoding
2174 | else {
2175 | throw new java.io.IOException("Base64 input not properly padded.");
2176 | } // end else: decoding
2177 | } // end if: buffer partially full
2178 |
2179 | } // end flush
2180 |
2181 | /**
2182 | * Flushes and closes (I think, in the superclass) the stream.
2183 | *
2184 | * @since 1.3
2185 | */
2186 | @Override
2187 | public void close() throws java.io.IOException {
2188 | // 1. Ensure that pending characters are written
2189 | flushBase64();
2190 |
2191 | // 2. Actually close the stream
2192 | // Base class both flushes and closes.
2193 | super.close();
2194 |
2195 | buffer = null;
2196 | out = null;
2197 | } // end close
2198 |
2199 | /**
2200 | * Suspends encoding of the stream. May be helpful if you need to embed
2201 | * a piece of base64-encoded data in a stream.
2202 | *
2203 | * @throws java.io.IOException
2204 | * if there's an error flushing
2205 | * @since 1.5.1
2206 | */
2207 | public void suspendEncoding() throws java.io.IOException {
2208 | flushBase64();
2209 | this.suspendEncoding = true;
2210 | } // end suspendEncoding
2211 |
2212 | /**
2213 | * Resumes encoding of the stream. May be helpful if you need to embed a
2214 | * piece of base64-encoded data in a stream.
2215 | *
2216 | * @since 1.5.1
2217 | */
2218 | public void resumeEncoding() {
2219 | this.suspendEncoding = false;
2220 | } // end resumeEncoding
2221 |
2222 | } // end inner class OutputStream
2223 |
2224 | } // end class Base64
2225 |
--------------------------------------------------------------------------------
/src/test/java/org/kamranzafar/jddl/AndroidExample.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012 Kamran Zafar
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.kamranzafar.jddl;
19 |
20 | import java.io.File;
21 | import java.io.FileOutputStream;
22 | import java.net.URL;
23 |
24 | import android.app.Activity;
25 | import android.app.Dialog;
26 | import android.app.ProgressDialog;
27 | import android.content.DialogInterface;
28 | import android.os.Bundle;
29 | import android.os.Environment;
30 | import android.os.Handler;
31 | import android.os.Message;
32 |
33 | /**
34 | * @author Kamran
35 | *
36 | * This is just an template/example that shows how jddl can be used in
37 | * Android apps, the following code should be updated as per
38 | * requirements.
39 | *
40 | */
41 | public class AndroidExample extends Activity {
42 | public static final int DOWNLOAD_PROGRESS_DIALOG_ID = 0;
43 |
44 | private DirectDownloader dd = new DirectDownloader();
45 |
46 | private ProgressDialog mProgressDialog;
47 |
48 | File downloadDir = new File(Environment.getExternalStorageDirectory()
49 | + "/Android/data/org.kamranzafar.android.test/files");
50 |
51 | private final Handler callback = new Handler() {
52 | @Override
53 | public void handleMessage(Message msg) {
54 | // TODO: update UI etc.
55 | };
56 | };
57 |
58 | @Override
59 | public void onCreate(Bundle savedInstanceState) {
60 | super.onCreate(savedInstanceState);
61 |
62 | // TODO: initializations etc.
63 | // this should be updated as per requirements
64 |
65 | downloadDir.mkdirs();
66 |
67 | // start downloading... this can be called from button clicks etc.
68 | new DownloadFileAsync().download("http://www.python.org/ftp/python/3.2.2/python-3.2.2.msi");
69 | }
70 |
71 | @Override
72 | protected Dialog onCreateDialog(int id) {
73 | switch (id) {
74 | case DOWNLOAD_PROGRESS_DIALOG_ID:
75 | mProgressDialog = new ProgressDialog(this);
76 | mProgressDialog.setMessage("Downloading...");
77 | mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
78 | mProgressDialog.setCancelable(true);
79 | mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
80 | public void onCancel(DialogInterface dialog) {
81 | dd.cancelAll();
82 | }
83 | });
84 | mProgressDialog.show();
85 |
86 | return mProgressDialog;
87 | default:
88 | return null;
89 | }
90 | }
91 |
92 | /**
93 | * shutdown jddl
94 | */
95 | @Override
96 | protected void onPause() {
97 | dd.shutdown();
98 | dd = null;
99 |
100 | super.onPause();
101 | }
102 |
103 | /**
104 | * start jddl
105 | */
106 | @Override
107 | protected void onResume() {
108 | super.onResume();
109 |
110 | dd = new DirectDownloader();
111 | new Thread(dd).start();
112 | }
113 |
114 | // This is an example download listner, that can be used to update UI and to
115 | // track download progress
116 | class DownloadFileAsync implements DownloadListener {
117 | private int fsize = -1;
118 | private String fname;
119 |
120 | private File downloadFile;
121 |
122 | public String download(String url) {
123 | fname = url.substring(url.lastIndexOf('/') + 1);
124 | downloadFile = new File(downloadDir, fname);
125 | try {
126 | DownloadTask dt = new DownloadTask(new URL(url), new FileOutputStream(downloadFile), this);
127 | dd.download(dt);
128 | } catch (Exception e) {
129 | e.printStackTrace();
130 | } finally {
131 | // show progress dialog
132 | showDialog(DOWNLOAD_PROGRESS_DIALOG_ID);
133 | }
134 | return null;
135 | }
136 |
137 | public void onComplete() {
138 | // dismiss progress dialog
139 | if (mProgressDialog.isShowing()) {
140 | mProgressDialog.dismiss();
141 | }
142 |
143 | // an example call back to update UI etc.
144 | Bundle b = new Bundle();
145 | b.putString("my_message", "download complete");
146 |
147 | Message m = new Message();
148 | m.setData(b);
149 |
150 | callback.sendMessage(m);
151 | }
152 |
153 | public void onStart(String fname, int arg0) {
154 | fsize = arg0;
155 | }
156 |
157 | public void onUpdate(int arg0, int arg1) {
158 | // update progress dialog
159 | mProgressDialog.setProgress((arg1 * 100) / fsize);
160 | }
161 |
162 | public void onCancel() {
163 | // delete partly downloaded file if the download is cancelled.
164 | if (downloadFile.exists()) {
165 | downloadFile.delete();
166 | }
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/src/test/java/org/kamranzafar/jddl/SimpleTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012 Kamran Zafar
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.kamranzafar.jddl;
19 |
20 | import java.io.FileNotFoundException;
21 | import java.io.FileOutputStream;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 |
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.junit.runners.JUnit4;
28 |
29 | /**
30 | * @author kamran
31 | *
32 | */
33 | @RunWith(JUnit4.class)
34 | public class SimpleTest {
35 | DirectDownloader dd = new DirectDownloader();
36 |
37 | @Test
38 | public void testSimple() throws MalformedURLException, FileNotFoundException, InterruptedException {
39 | final Thread t = new Thread(dd);
40 | final String file = "http://python.org/ftp/python/2.7.2/python-2.7.2.msi";
41 | final String f = "target/" + file.substring(file.lastIndexOf('/') + 1);
42 |
43 | DownloadTask dt = new DownloadTask(new URL(file), new FileOutputStream(f)).addListener(new DownloadListener() {
44 | int size;
45 |
46 | public void onUpdate(int bytes, int totalDownloaded) {
47 | updateProgress((double) totalDownloaded / size);
48 | }
49 |
50 | public void onStart(String fname, int size) {
51 | System.out.println("Downloading " + fname + " of size " + size);
52 | this.size = size;
53 | updateProgress(0);
54 | }
55 |
56 | public void onComplete() {
57 | System.out.println("\n" + f + " downloaded");
58 | }
59 |
60 | public void onCancel() {
61 |
62 | }
63 | });
64 |
65 | dd.download(dt);
66 |
67 | t.start();
68 | t.join();
69 | }
70 |
71 | void updateProgress(double progressPercentage) {
72 | final int width = 50;
73 |
74 | System.out.print("\r[");
75 | int i = 0;
76 | for (; i <= (int) (progressPercentage * width); i++) {
77 | System.out.print(".");
78 | }
79 | for (; i < width; i++) {
80 | System.out.print(" ");
81 | }
82 | System.out.print("]");
83 | }
84 |
85 | public static void main(String[] args) throws MalformedURLException, FileNotFoundException, InterruptedException {
86 | SimpleTest st = new SimpleTest();
87 | st.testSimple();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/test/java/org/kamranzafar/jddl/SwingTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012 Kamran Zafar
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package org.kamranzafar.jddl;
19 |
20 | import java.awt.Container;
21 | import java.awt.GridLayout;
22 | import java.awt.event.ActionEvent;
23 | import java.awt.event.ActionListener;
24 | import java.io.File;
25 | import java.io.FileOutputStream;
26 | import java.io.IOException;
27 | import java.net.URL;
28 |
29 | import javax.swing.BorderFactory;
30 | import javax.swing.BoxLayout;
31 | import javax.swing.JButton;
32 | import javax.swing.JFrame;
33 | import javax.swing.JPanel;
34 | import javax.swing.JProgressBar;
35 | import javax.swing.border.TitledBorder;
36 |
37 | import org.junit.Test;
38 | import org.junit.runner.RunWith;
39 | import org.junit.runners.JUnit4;
40 |
41 | /**
42 | * @author kamran
43 | *
44 | */
45 | @RunWith(JUnit4.class)
46 | public class SwingTest {
47 | private static final String STOP = "Stop";
48 | private static final String CANCEL = "Cancel";
49 | private static final String PAUSE = "Pause";
50 | private static final String RESUME = "Resume";
51 | private static final String TOTAL = "Total";
52 |
53 | // total progress bar
54 | private final JProgressBar totalProgressBar = new JProgressBar();
55 |
56 | @Test
57 | public void testSwing() throws IOException, InterruptedException {
58 | JFrame f = new JFrame("jddl Swing example");
59 | f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
60 | Container content = f.getContentPane();
61 | content.setLayout(new GridLayout(4, 1));
62 |
63 | // Some files
64 | String files[] = { "http://python.org/ftp/python/2.7.2/python-2.7.2.msi",
65 | "http://www.python.org/ftp/python/3.2.2/python-3.2.2.msi",
66 | "http://www.python.org/ftp/python/3.2.2/python-3.2.2.amd64.msi" };
67 | // Create a DirectDownloader instance
68 | final DirectDownloader fd = new DirectDownloader();
69 |
70 | // Progress bars for individual file downloads
71 | JProgressBar[] progressBar = new JProgressBar[3];
72 |
73 | // Pause/Resume buttons
74 | JButton[] pauseButton = new JButton[3];
75 |
76 | // Cancel
77 | JButton[] cancelButton = new JButton[3];
78 |
79 | JButton stopButton = new JButton(STOP);
80 | stopButton.addActionListener(new ActionListener() {
81 | public void actionPerformed(ActionEvent e) {
82 | fd.cancelAll();
83 | }
84 | });
85 |
86 | // Initialize progress bars and create download tasks
87 | for (int i = 0; i < 3; i++) {
88 | String fname = files[i].substring(files[i].lastIndexOf('/') + 1);
89 |
90 | progressBar[i] = new JProgressBar();
91 | pauseButton[i] = new JButton(PAUSE);
92 | cancelButton[i] = new JButton(CANCEL);
93 |
94 | JPanel panel = new JPanel();
95 | BoxLayout box = new BoxLayout(panel, BoxLayout.X_AXIS);
96 |
97 | panel.setLayout(box);
98 |
99 | final DownloadTask dt = new DownloadTask(new URL(files[i]), new FileOutputStream(fname));
100 | dt.addListener(new ProgressBarUpdator(fname, progressBar[i]));
101 |
102 | pauseButton[i].addActionListener(new ActionListener() {
103 | public void actionPerformed(ActionEvent e) {
104 | if (dt.isPaused()) {
105 | dt.setPaused(false);
106 | ((JButton) e.getSource()).setText(PAUSE);
107 | } else {
108 | dt.setPaused(true);
109 | ((JButton) e.getSource()).setText(RESUME);
110 | }
111 | }
112 | });
113 |
114 | cancelButton[i].addActionListener(new ActionListener() {
115 | public void actionPerformed(ActionEvent arg0) {
116 | dt.setCancelled(true);
117 | }
118 | });
119 |
120 | progressBar[i].setStringPainted(true);
121 | progressBar[i].setBorder(BorderFactory.createTitledBorder("Downloading " + fname + "..."));
122 |
123 | panel.add(progressBar[i]);
124 | panel.add(pauseButton[i]);
125 | panel.add(cancelButton[i]);
126 |
127 | content.add(panel);
128 |
129 | fd.download(dt);
130 | }
131 |
132 | totalProgressBar.setBorder(BorderFactory.createTitledBorder(TOTAL));
133 | totalProgressBar.setStringPainted(true);
134 | totalProgressBar.setMaximum(0);
135 |
136 | JPanel panel = new JPanel();
137 | BoxLayout box = new BoxLayout(panel, BoxLayout.X_AXIS);
138 |
139 | panel.setLayout(box);
140 | panel.add(totalProgressBar);
141 | panel.add(stopButton);
142 |
143 | content.add(panel);
144 |
145 | f.setSize(400, 200);
146 | f.setVisible(true);
147 |
148 | // Start downloading
149 | Thread t = new Thread(fd);
150 | t.start();
151 | }
152 |
153 | // Class that updates the download progress
154 | class ProgressBarUpdator implements DownloadListener {
155 | private static final String DONE = "Done";
156 | JProgressBar progressBar;
157 | int size = -1;
158 |
159 | String fname;
160 |
161 | public ProgressBarUpdator(String fname, JProgressBar progressBar) {
162 | this.progressBar = progressBar;
163 | this.fname = fname;
164 | }
165 |
166 | public void onComplete() {
167 | if (size == -1) {
168 | progressBar.setIndeterminate(false);
169 | progressBar.setValue(100);
170 | }
171 |
172 | ((TitledBorder) progressBar.getBorder()).setTitle(DONE);
173 | progressBar.repaint();
174 | }
175 |
176 | public void onStart(String fname, int fsize) {
177 | if (fsize > -1) {
178 | progressBar.setMaximum(fsize);
179 |
180 | synchronized (totalProgressBar) {
181 | totalProgressBar.setMaximum(totalProgressBar.getMaximum() + fsize);
182 | }
183 |
184 | size = fsize;
185 | } else {
186 | progressBar.setIndeterminate(true);
187 | }
188 | }
189 |
190 | public void onUpdate(int bytes, int totalDownloaded) {
191 | if (size == -1) {
192 | progressBar.setString("" + totalDownloaded);
193 | } else {
194 | progressBar.setValue(totalDownloaded);
195 |
196 | synchronized (totalProgressBar) {
197 | totalProgressBar.setValue(totalProgressBar.getValue() + bytes);
198 | }
199 | }
200 | }
201 |
202 | public void onCancel() {
203 | new File(fname).delete();
204 | }
205 | }
206 |
207 | public static void main(String[] args) throws IOException, InterruptedException {
208 | SwingTest st = new SwingTest();
209 | st.testSwing();
210 | }
211 | }
212 |
--------------------------------------------------------------------------------