Encodes and decodes to and from Base64 notation.
6 | *Homepage: http://iharder.net/base64.
7 | * 8 | *Example:
9 | * 10 | *String encoded = Base64.encode( myByteArray );
11 | * byte[] myByteArray = Base64.decode( encoded );
13 | *
14 | * The options parameter, which appears in a few places, is used to pass 15 | * several pieces of information to the encoder. In the "higher level" methods such as 16 | * encodeBytes( bytes, options ) the options parameter can be used to indicate such 17 | * things as first gzipping the bytes before encoding them, not inserting linefeeds, 18 | * and encoding using the URL-safe and Ordered dialects.
19 | * 20 | *Note, according to RFC3548, 21 | * Section 2.1, implementations should not add line feeds unless explicitly told 22 | * to do so. I've got Base64 set to this behavior now, although earlier versions 23 | * broke lines by default.
24 | * 25 | *The constants defined in Base64 can be OR-ed together to combine options, so you 26 | * might make a call like this:
27 | * 28 | *String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );
29 | * to compress the data before encoding it and then making the output have newline characters.
30 | *Also...
31 | *String encoded = Base64.encodeBytes( crazyString.getBytes() );
32 | *
33 | *
34 | *
35 | * 36 | * Change Log: 37 | *
38 | *131 | * I am placing this code in the Public Domain. Do with it as you will. 132 | * This software comes with no guarantees or warranties but with 133 | * plenty of well-wishing instead! 134 | * Please visit http://iharder.net/base64 135 | * periodically to check for updates or to contribute improvements. 136 | *
137 | * 138 | * @author Robert Harder 139 | * @author rob@iharder.net 140 | * @version 2.3.5 141 | */ 142 | public class Base64 143 | { 144 | 145 | /* ******** P U B L I C F I E L D S ******** */ 146 | 147 | 148 | /** No options specified. Value is zero. */ 149 | public final static int NO_OPTIONS = 0; 150 | 151 | /** Specify encoding in first bit. Value is one. */ 152 | public final static int ENCODE = 1; 153 | 154 | 155 | /** Specify decoding in first bit. Value is zero. */ 156 | public final static int DECODE = 0; 157 | 158 | 159 | /** Specify that data should be gzip-compressed in second bit. Value is two. */ 160 | public final static int GZIP = 2; 161 | 162 | /** Specify that gzipped data should not be automatically gunzipped. */ 163 | public final static int DONT_GUNZIP = 4; 164 | 165 | 166 | /** Do break lines when encoding. Value is 8. */ 167 | public final static int DO_BREAK_LINES = 8; 168 | 169 | /** 170 | * Encode using Base64-like encoding that is URL- and Filename-safe as described 171 | * in Section 4 of RFC3548: 172 | * http://www.faqs.org/rfcs/rfc3548.html. 173 | * It is important to note that data encoded this way is not officially valid Base64, 174 | * or at the very least should not be called Base64 without also specifying that is 175 | * was encoded using the URL- and Filename-safe dialect. 176 | */ 177 | public final static int URL_SAFE = 16; 178 | 179 | 180 | /** 181 | * Encode using the special "ordered" dialect of Base64 described here: 182 | * http://www.faqs.org/qa/rfcc-1940.html. 183 | */ 184 | public final static int ORDERED = 32; 185 | 186 | 187 | /* ******** P R I V A T E F I E L D S ******** */ 188 | 189 | 190 | /** Maximum line length (76) of Base64 output. */ 191 | private final static int MAX_LINE_LENGTH = 76; 192 | 193 | 194 | /** The equals sign (=) as a byte. */ 195 | private final static byte EQUALS_SIGN = (byte)'='; 196 | 197 | 198 | /** The new line character (\n) as a byte. */ 199 | private final static byte NEW_LINE = (byte)'\n'; 200 | 201 | 202 | /** Preferred encoding. */ 203 | private final static String PREFERRED_ENCODING = "US-ASCII"; 204 | 205 | 206 | private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding 207 | private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding 208 | 209 | 210 | /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ 211 | 212 | /** The 64 valid Base64 values. */ 213 | /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ 214 | private final static byte[] _STANDARD_ALPHABET = { 215 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 216 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 217 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 218 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 219 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 220 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 221 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 222 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 223 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 224 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' 225 | }; 226 | 227 | 228 | /** 229 | * Translates a Base64 value to either its 6-bit reconstruction value 230 | * or a negative number indicating some other meaning. 231 | **/ 232 | private final static byte[] _STANDARD_DECODABET = { 233 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 234 | -5,-5, // Whitespace: Tab and Linefeed 235 | -9,-9, // Decimal 11 - 12 236 | -5, // Whitespace: Carriage Return 237 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 238 | -9,-9,-9,-9,-9, // Decimal 27 - 31 239 | -5, // Whitespace: Space 240 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 241 | 62, // Plus sign at decimal 43 242 | -9,-9,-9, // Decimal 44 - 46 243 | 63, // Slash at decimal 47 244 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 245 | -9,-9,-9, // Decimal 58 - 60 246 | -1, // Equals sign at decimal 61 247 | -9,-9,-9, // Decimal 62 - 64 248 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 249 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 250 | -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 251 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 252 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 253 | -9,-9,-9,-9 // Decimal 123 - 126 254 | /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 255 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 256 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 257 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 258 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 259 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 260 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 261 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 262 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 263 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ 264 | }; 265 | 266 | 267 | /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ 268 | 269 | /** 270 | * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 271 | * http://www.faqs.org/rfcs/rfc3548.html. 272 | * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." 273 | */ 274 | private final static byte[] _URL_SAFE_ALPHABET = { 275 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 276 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 277 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 278 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 279 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 280 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 281 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 282 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 283 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 284 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' 285 | }; 286 | 287 | /** 288 | * Used in decoding URL- and Filename-safe dialects of Base64. 289 | */ 290 | private final static byte[] _URL_SAFE_DECODABET = { 291 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 292 | -5,-5, // Whitespace: Tab and Linefeed 293 | -9,-9, // Decimal 11 - 12 294 | -5, // Whitespace: Carriage Return 295 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 296 | -9,-9,-9,-9,-9, // Decimal 27 - 31 297 | -5, // Whitespace: Space 298 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 299 | -9, // Plus sign at decimal 43 300 | -9, // Decimal 44 301 | 62, // Minus sign at decimal 45 302 | -9, // Decimal 46 303 | -9, // Slash at decimal 47 304 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 305 | -9,-9,-9, // Decimal 58 - 60 306 | -1, // Equals sign at decimal 61 307 | -9,-9,-9, // Decimal 62 - 64 308 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 309 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 310 | -9,-9,-9,-9, // Decimal 91 - 94 311 | 63, // Underscore at decimal 95 312 | -9, // Decimal 96 313 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 314 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 315 | -9,-9,-9,-9 // Decimal 123 - 126 316 | /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 317 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 318 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 319 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 320 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 321 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 322 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 323 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 324 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 325 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ 326 | }; 327 | 328 | 329 | 330 | /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ 331 | 332 | /** 333 | * I don't get the point of this technique, but someone requested it, 334 | * and it is described here: 335 | * http://www.faqs.org/qa/rfcc-1940.html. 336 | */ 337 | private final static byte[] _ORDERED_ALPHABET = { 338 | (byte)'-', 339 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', 340 | (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', 341 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 342 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 343 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 344 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 345 | (byte)'_', 346 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 347 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 348 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 349 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' 350 | }; 351 | 352 | /** 353 | * Used in decoding the "ordered" dialect of Base64. 354 | */ 355 | private final static byte[] _ORDERED_DECODABET = { 356 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 357 | -5,-5, // Whitespace: Tab and Linefeed 358 | -9,-9, // Decimal 11 - 12 359 | -5, // Whitespace: Carriage Return 360 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 361 | -9,-9,-9,-9,-9, // Decimal 27 - 31 362 | -5, // Whitespace: Space 363 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 364 | -9, // Plus sign at decimal 43 365 | -9, // Decimal 44 366 | 0, // Minus sign at decimal 45 367 | -9, // Decimal 46 368 | -9, // Slash at decimal 47 369 | 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine 370 | -9,-9,-9, // Decimal 58 - 60 371 | -1, // Equals sign at decimal 61 372 | -9,-9,-9, // Decimal 62 - 64 373 | 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' 374 | 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' 375 | -9,-9,-9,-9, // Decimal 91 - 94 376 | 37, // Underscore at decimal 95 377 | -9, // Decimal 96 378 | 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' 379 | 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' 380 | -9,-9,-9,-9 // Decimal 123 - 126 381 | /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 382 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 383 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 384 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 385 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 386 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 387 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 388 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 389 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 390 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ 391 | }; 392 | 393 | 394 | /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ 395 | 396 | 397 | /** 398 | * Returns one of the _SOMETHING_ALPHABET byte arrays depending on 399 | * the options specified. 400 | * It's possible, though silly, to specify ORDERED and URLSAFE 401 | * in which case one of them will be picked, though there is 402 | * no guarantee as to which one will be picked. 403 | */ 404 | private final static byte[] getAlphabet( int options ) { 405 | if ((options & URL_SAFE) == URL_SAFE) { 406 | return _URL_SAFE_ALPHABET; 407 | } else if ((options & ORDERED) == ORDERED) { 408 | return _ORDERED_ALPHABET; 409 | } else { 410 | return _STANDARD_ALPHABET; 411 | } 412 | } // end getAlphabet 413 | 414 | 415 | /** 416 | * Returns one of the _SOMETHING_DECODABET byte arrays depending on 417 | * the options specified. 418 | * It's possible, though silly, to specify ORDERED and URL_SAFE 419 | * in which case one of them will be picked, though there is 420 | * no guarantee as to which one will be picked. 421 | */ 422 | private final static byte[] getDecodabet( int options ) { 423 | if( (options & URL_SAFE) == URL_SAFE) { 424 | return _URL_SAFE_DECODABET; 425 | } else if ((options & ORDERED) == ORDERED) { 426 | return _ORDERED_DECODABET; 427 | } else { 428 | return _STANDARD_DECODABET; 429 | } 430 | } // end getAlphabet 431 | 432 | 433 | 434 | /** Defeats instantiation. */ 435 | private Base64(){} 436 | 437 | 438 | 439 | 440 | /* ******** E N C O D I N G M E T H O D S ******** */ 441 | 442 | 443 | /** 444 | * Encodes up to the first three bytes of array threeBytes 445 | * and returns a four-byte array in Base64 notation. 446 | * The actual number of significant bytes in your array is 447 | * given by numSigBytes. 448 | * The array threeBytes needs only be as big as 449 | * numSigBytes. 450 | * Code can reuse a byte array by passing a four-byte array as b4. 451 | * 452 | * @param b4 A reusable byte array to reduce array instantiation 453 | * @param threeBytes the array to convert 454 | * @param numSigBytes the number of significant bytes in your array 455 | * @return four byte array in Base64 notation. 456 | * @since 1.5.1 457 | */ 458 | private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { 459 | encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); 460 | return b4; 461 | } // end encode3to4 462 | 463 | 464 | /** 465 | *Encodes up to three bytes of the array source 466 | * and writes the resulting four Base64 bytes to destination. 467 | * The source and destination arrays can be manipulated 468 | * anywhere along their length by specifying 469 | * srcOffset and destOffset. 470 | * This method does not check to make sure your arrays 471 | * are large enough to accomodate srcOffset + 3 for 472 | * the source array or destOffset + 4 for 473 | * the destination array. 474 | * The actual number of significant bytes in your array is 475 | * given by numSigBytes.
476 | *This is the lowest level of the encoding methods with 477 | * all possible parameters.
478 | * 479 | * @param source the array to convert 480 | * @param srcOffset the index where conversion begins 481 | * @param numSigBytes the number of significant bytes in your array 482 | * @param destination the array to hold the conversion 483 | * @param destOffset the index where output will be put 484 | * @return the destination array 485 | * @since 1.3 486 | */ 487 | private static byte[] encode3to4( 488 | byte[] source, int srcOffset, int numSigBytes, 489 | byte[] destination, int destOffset, int options ) { 490 | 491 | byte[] ALPHABET = getAlphabet( options ); 492 | 493 | // 1 2 3 494 | // 01234567890123456789012345678901 Bit position 495 | // --------000000001111111122222222 Array position from threeBytes 496 | // --------| || || || | Six bit groups to index ALPHABET 497 | // >>18 >>12 >> 6 >> 0 Right shift necessary 498 | // 0x3f 0x3f 0x3f Additional AND 499 | 500 | // Create buffer with zero-padding if there are only one or two 501 | // significant bytes passed in the array. 502 | // We have to shift left 24 in order to flush out the 1's that appear 503 | // when Java treats a value as negative that is cast from a byte to an int. 504 | int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) 505 | | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) 506 | | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); 507 | 508 | switch( numSigBytes ) 509 | { 510 | case 3: 511 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 512 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 513 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 514 | destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; 515 | return destination; 516 | 517 | case 2: 518 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 519 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 520 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 521 | destination[ destOffset + 3 ] = EQUALS_SIGN; 522 | return destination; 523 | 524 | case 1: 525 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 526 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 527 | destination[ destOffset + 2 ] = EQUALS_SIGN; 528 | destination[ destOffset + 3 ] = EQUALS_SIGN; 529 | return destination; 530 | 531 | default: 532 | return destination; 533 | } // end switch 534 | } // end encode3to4 535 | 536 | 537 | 538 | /** 539 | * Performs Base64 encoding on theraw
ByteBuffer,
540 | * writing it to the encoded
ByteBuffer.
541 | * This is an experimental feature. Currently it does not
542 | * pass along any options (such as {@link #DO_BREAK_LINES}
543 | * or {@link #GZIP}.
544 | *
545 | * @param raw input buffer
546 | * @param encoded output buffer
547 | * @since 2.3
548 | */
549 | public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){
550 | byte[] raw3 = new byte[3];
551 | byte[] enc4 = new byte[4];
552 |
553 | while( raw.hasRemaining() ){
554 | int rem = Math.min(3,raw.remaining());
555 | raw.get(raw3,0,rem);
556 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
557 | encoded.put(enc4);
558 | } // end input remaining
559 | }
560 |
561 |
562 | /**
563 | * Performs Base64 encoding on the raw
ByteBuffer,
564 | * writing it to the encoded
CharBuffer.
565 | * This is an experimental feature. Currently it does not
566 | * pass along any options (such as {@link #DO_BREAK_LINES}
567 | * or {@link #GZIP}.
568 | *
569 | * @param raw input buffer
570 | * @param encoded output buffer
571 | * @since 2.3
572 | */
573 | public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){
574 | byte[] raw3 = new byte[3];
575 | byte[] enc4 = new byte[4];
576 |
577 | while( raw.hasRemaining() ){
578 | int rem = Math.min(3,raw.remaining());
579 | raw.get(raw3,0,rem);
580 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
581 | for( int i = 0; i < 4; i++ ){
582 | encoded.put( (char)(enc4[i] & 0xFF) );
583 | }
584 | } // end input remaining
585 | }
586 |
587 |
588 |
589 |
590 | /**
591 | * Serializes an object and returns the Base64-encoded
592 | * version of that serialized object.
593 | *
594 | * As of v 2.3, if the object 595 | * cannot be serialized or there is another error, 596 | * the method will throw an java.io.IOException. This is new to v2.3! 597 | * In earlier versions, it just returned a null value, but 598 | * in retrospect that's a pretty poor way to handle it.
599 | * 600 | * The object is not GZip-compressed before being encoded. 601 | * 602 | * @param serializableObject The object to encode 603 | * @return The Base64-encoded object 604 | * @throws java.io.IOException if there is an error 605 | * @throws NullPointerException if serializedObject is null 606 | * @since 1.4 607 | */ 608 | public static String encodeObject( java.io.Serializable serializableObject ) 609 | throws java.io.IOException { 610 | return encodeObject( serializableObject, NO_OPTIONS ); 611 | } // end encodeObject 612 | 613 | 614 | 615 | /** 616 | * Serializes an object and returns the Base64-encoded 617 | * version of that serialized object. 618 | * 619 | *As of v 2.3, if the object 620 | * cannot be serialized or there is another error, 621 | * the method will throw an java.io.IOException. This is new to v2.3! 622 | * In earlier versions, it just returned a null value, but 623 | * in retrospect that's a pretty poor way to handle it.
624 | * 625 | * The object is not GZip-compressed before being encoded. 626 | *627 | * Example options:
628 | * GZIP: gzip-compresses object before encoding it. 629 | * DO_BREAK_LINES: break lines at 76 characters 630 | *631 | *
632 | * Example: encodeObject( myObj, Base64.GZIP )
or
633 | *
634 | * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )
635 | *
636 | * @param serializableObject The object to encode
637 | * @param options Specified options
638 | * @return The Base64-encoded object
639 | * @see Base64#GZIP
640 | * @see Base64#DO_BREAK_LINES
641 | * @throws java.io.IOException if there is an error
642 | * @since 2.0
643 | */
644 | public static String encodeObject( java.io.Serializable serializableObject, int options )
645 | throws java.io.IOException {
646 |
647 | if( serializableObject == null ){
648 | throw new NullPointerException( "Cannot serialize a null object." );
649 | } // end if: null
650 |
651 | // Streams
652 | java.io.ByteArrayOutputStream baos = null;
653 | java.io.OutputStream b64os = null;
654 | java.util.zip.GZIPOutputStream gzos = null;
655 | java.io.ObjectOutputStream oos = null;
656 |
657 |
658 | try {
659 | // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
660 | baos = new java.io.ByteArrayOutputStream();
661 | b64os = new Base64.OutputStream( baos, ENCODE | options );
662 | if( (options & GZIP) != 0 ){
663 | // Gzip
664 | gzos = new java.util.zip.GZIPOutputStream(b64os);
665 | oos = new java.io.ObjectOutputStream( gzos );
666 | } else {
667 | // Not gzipped
668 | oos = new java.io.ObjectOutputStream( b64os );
669 | }
670 | oos.writeObject( serializableObject );
671 | } // end try
672 | catch( java.io.IOException e ) {
673 | // Catch it and then throw it immediately so that
674 | // the finally{} block is called for cleanup.
675 | throw e;
676 | } // end catch
677 | finally {
678 | try{ oos.close(); } catch( Exception e ){}
679 | try{ gzos.close(); } catch( Exception e ){}
680 | try{ b64os.close(); } catch( Exception e ){}
681 | try{ baos.close(); } catch( Exception e ){}
682 | } // end finally
683 |
684 | // Return value according to relevant encoding.
685 | try {
686 | return new String( baos.toByteArray(), PREFERRED_ENCODING );
687 | } // end try
688 | catch (java.io.UnsupportedEncodingException uue){
689 | // Fall back to some Java default
690 | return new String( baos.toByteArray() );
691 | } // end catch
692 |
693 | } // end encode
694 |
695 |
696 |
697 | /**
698 | * Encodes a byte array into Base64 notation.
699 | * Does not GZip-compress data.
700 | *
701 | * @param source The data to convert
702 | * @return The data in Base64-encoded form
703 | * @throws NullPointerException if source array is null
704 | * @since 1.4
705 | */
706 | public static String encodeBytes( byte[] source ) {
707 | // Since we're not going to have the GZIP encoding turned on,
708 | // we're not going to have an java.io.IOException thrown, so
709 | // we should not force the user to have to catch it.
710 | String encoded = null;
711 | try {
712 | encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
713 | } catch (java.io.IOException ex) {
714 | assert false : ex.getMessage();
715 | } // end catch
716 | assert encoded != null;
717 | return encoded;
718 | } // end encodeBytes
719 |
720 |
721 |
722 | /**
723 | * Encodes a byte array into Base64 notation.
724 | *
725 | * Example options:
726 | * GZIP: gzip-compresses object before encoding it. 727 | * DO_BREAK_LINES: break lines at 76 characters 728 | * Note: Technically, this makes your encoding non-compliant. 729 | *730 | *
731 | * Example: encodeBytes( myData, Base64.GZIP )
or
732 | *
733 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
734 | *
735 | *
736 | *
As of v 2.3, if there is an error with the GZIP stream, 737 | * the method will throw an java.io.IOException. This is new to v2.3! 738 | * In earlier versions, it just returned a null value, but 739 | * in retrospect that's a pretty poor way to handle it.
740 | * 741 | * 742 | * @param source The data to convert 743 | * @param options Specified options 744 | * @return The Base64-encoded data as a String 745 | * @see Base64#GZIP 746 | * @see Base64#DO_BREAK_LINES 747 | * @throws java.io.IOException if there is an error 748 | * @throws NullPointerException if source array is null 749 | * @since 2.0 750 | */ 751 | public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { 752 | return encodeBytes( source, 0, source.length, options ); 753 | } // end encodeBytes 754 | 755 | 756 | /** 757 | * Encodes a byte array into Base64 notation. 758 | * Does not GZip-compress data. 759 | * 760 | *As of v 2.3, if there is an error, 761 | * the method will throw an java.io.IOException. This is new to v2.3! 762 | * In earlier versions, it just returned a null value, but 763 | * in retrospect that's a pretty poor way to handle it.
764 | * 765 | * 766 | * @param source The data to convert 767 | * @param off Offset in array where conversion should begin 768 | * @param len Length of data to convert 769 | * @return The Base64-encoded data as a String 770 | * @throws NullPointerException if source array is null 771 | * @throws IllegalArgumentException if source array, offset, or length are invalid 772 | * @since 1.4 773 | */ 774 | public static String encodeBytes( byte[] source, int off, int len ) { 775 | // Since we're not going to have the GZIP encoding turned on, 776 | // we're not going to have an java.io.IOException thrown, so 777 | // we should not force the user to have to catch it. 778 | String encoded = null; 779 | try { 780 | encoded = encodeBytes( source, off, len, NO_OPTIONS ); 781 | } catch (java.io.IOException ex) { 782 | assert false : ex.getMessage(); 783 | } // end catch 784 | assert encoded != null; 785 | return encoded; 786 | } // end encodeBytes 787 | 788 | 789 | 790 | /** 791 | * Encodes a byte array into Base64 notation. 792 | *793 | * Example options:
794 | * GZIP: gzip-compresses object before encoding it. 795 | * DO_BREAK_LINES: break lines at 76 characters 796 | * Note: Technically, this makes your encoding non-compliant. 797 | *798 | *
799 | * Example: encodeBytes( myData, Base64.GZIP )
or
800 | *
801 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
802 | *
803 | *
804 | *
As of v 2.3, if there is an error with the GZIP stream, 805 | * the method will throw an java.io.IOException. This is new to v2.3! 806 | * In earlier versions, it just returned a null value, but 807 | * in retrospect that's a pretty poor way to handle it.
808 | * 809 | * 810 | * @param source The data to convert 811 | * @param off Offset in array where conversion should begin 812 | * @param len Length of data to convert 813 | * @param options Specified options 814 | * @return The Base64-encoded data as a String 815 | * @see Base64#GZIP 816 | * @see Base64#DO_BREAK_LINES 817 | * @throws java.io.IOException if there is an error 818 | * @throws NullPointerException if source array is null 819 | * @throws IllegalArgumentException if source array, offset, or length are invalid 820 | * @since 2.0 821 | */ 822 | public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { 823 | byte[] encoded = encodeBytesToBytes( source, off, len, options ); 824 | 825 | // Return value according to relevant encoding. 826 | try { 827 | return new String( encoded, PREFERRED_ENCODING ); 828 | } // end try 829 | catch (java.io.UnsupportedEncodingException uue) { 830 | return new String( encoded ); 831 | } // end catch 832 | 833 | } // end encodeBytes 834 | 835 | 836 | 837 | 838 | /** 839 | * Similar to {@link #encodeBytes(byte[])} but returns 840 | * a byte array instead of instantiating a String. This is more efficient 841 | * if you're working with I/O streams and have large data sets to encode. 842 | * 843 | * 844 | * @param source The data to convert 845 | * @return The Base64-encoded data as a byte[] (of ASCII characters) 846 | * @throws NullPointerException if source array is null 847 | * @since 2.3.1 848 | */ 849 | public static byte[] encodeBytesToBytes( byte[] source ) { 850 | byte[] encoded = null; 851 | try { 852 | encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); 853 | } catch( java.io.IOException ex ) { 854 | assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 855 | } 856 | return encoded; 857 | } 858 | 859 | 860 | /** 861 | * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns 862 | * a byte array instead of instantiating a String. This is more efficient 863 | * if you're working with I/O streams and have large data sets to encode. 864 | * 865 | * 866 | * @param source The data to convert 867 | * @param off Offset in array where conversion should begin 868 | * @param len Length of data to convert 869 | * @param options Specified options 870 | * @return The Base64-encoded data as a String 871 | * @see Base64#GZIP 872 | * @see Base64#DO_BREAK_LINES 873 | * @throws java.io.IOException if there is an error 874 | * @throws NullPointerException if source array is null 875 | * @throws IllegalArgumentException if source array, offset, or length are invalid 876 | * @since 2.3.1 877 | */ 878 | public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { 879 | 880 | if( source == null ){ 881 | throw new NullPointerException( "Cannot serialize a null array." ); 882 | } // end if: null 883 | 884 | if( off < 0 ){ 885 | throw new IllegalArgumentException( "Cannot have negative offset: " + off ); 886 | } // end if: off < 0 887 | 888 | if( len < 0 ){ 889 | throw new IllegalArgumentException( "Cannot have length offset: " + len ); 890 | } // end if: len < 0 891 | 892 | if( off + len > source.length ){ 893 | throw new IllegalArgumentException( 894 | "Cannot have offset of " + off + " and length of " + len + " with array of length " + source.length); 895 | } // end if: off < 0 896 | 897 | 898 | 899 | // Compress? 900 | if( (options & GZIP) != 0 ) { 901 | java.io.ByteArrayOutputStream baos = null; 902 | java.util.zip.GZIPOutputStream gzos = null; 903 | Base64.OutputStream b64os = null; 904 | 905 | try { 906 | // GZip -> Base64 -> ByteArray 907 | baos = new java.io.ByteArrayOutputStream(); 908 | b64os = new Base64.OutputStream( baos, ENCODE | options ); 909 | gzos = new java.util.zip.GZIPOutputStream( b64os ); 910 | 911 | gzos.write( source, off, len ); 912 | gzos.close(); 913 | } // end try 914 | catch( java.io.IOException e ) { 915 | // Catch it and then throw it immediately so that 916 | // the finally{} block is called for cleanup. 917 | throw e; 918 | } // end catch 919 | finally { 920 | try{ gzos.close(); } catch( Exception e ){} 921 | try{ b64os.close(); } catch( Exception e ){} 922 | try{ baos.close(); } catch( Exception e ){} 923 | } // end finally 924 | 925 | return baos.toByteArray(); 926 | } // end if: compress 927 | 928 | // Else, don't compress. Better not to use streams at all then. 929 | else { 930 | boolean breakLines = (options & DO_BREAK_LINES) > 0; 931 | 932 | //int len43 = len * 4 / 3; 933 | //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 934 | // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding 935 | // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines 936 | // Try to determine more precisely how big the array needs to be. 937 | // If we get it right, we don't have to do an array copy, and 938 | // we save a bunch of memory. 939 | int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding 940 | if( breakLines ){ 941 | encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters 942 | } 943 | byte[] outBuff = new byte[ encLen ]; 944 | 945 | 946 | int d = 0; 947 | int e = 0; 948 | int len2 = len - 2; 949 | int lineLength = 0; 950 | for( ; d < len2; d+=3, e+=4 ) { 951 | encode3to4( source, d+off, 3, outBuff, e, options ); 952 | 953 | lineLength += 4; 954 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) 955 | { 956 | outBuff[e+4] = NEW_LINE; 957 | e++; 958 | lineLength = 0; 959 | } // end if: end of line 960 | } // en dfor: each piece of array 961 | 962 | if( d < len ) { 963 | encode3to4( source, d+off, len - d, outBuff, e, options ); 964 | e += 4; 965 | } // end if: some padding needed 966 | 967 | 968 | // Only resize array if we didn't guess it right. 969 | if( e < outBuff.length - 1 ){ 970 | byte[] finalOut = new byte[e]; 971 | System.arraycopy(outBuff,0, finalOut,0,e); 972 | //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); 973 | return finalOut; 974 | } else { 975 | //System.err.println("No need to resize array."); 976 | return outBuff; 977 | } 978 | 979 | } // end else: don't compress 980 | 981 | } // end encodeBytesToBytes 982 | 983 | 984 | 985 | 986 | 987 | /* ******** D E C O D I N G M E T H O D S ******** */ 988 | 989 | 990 | /** 991 | * Decodes four bytes from array source 992 | * and writes the resulting bytes (up to three of them) 993 | * to destination. 994 | * The source and destination arrays can be manipulated 995 | * anywhere along their length by specifying 996 | * srcOffset and destOffset. 997 | * This method does not check to make sure your arrays 998 | * are large enough to accomodate srcOffset + 4 for 999 | * the source array or destOffset + 3 for 1000 | * the destination array. 1001 | * This method returns the actual number of bytes that 1002 | * were converted from the Base64 encoding. 1003 | *This is the lowest level of the decoding methods with 1004 | * all possible parameters.
1005 | * 1006 | * 1007 | * @param source the array to convert 1008 | * @param srcOffset the index where conversion begins 1009 | * @param destination the array to hold the conversion 1010 | * @param destOffset the index where output will be put 1011 | * @param options alphabet type is pulled from this (standard, url-safe, ordered) 1012 | * @return the number of decoded bytes converted 1013 | * @throws NullPointerException if source or destination arrays are null 1014 | * @throws IllegalArgumentException if srcOffset or destOffset are invalid 1015 | * or there is not enough room in the array. 1016 | * @since 1.3 1017 | */ 1018 | private static int decode4to3( 1019 | byte[] source, int srcOffset, 1020 | byte[] destination, int destOffset, int options ) { 1021 | 1022 | // Lots of error checking and exception throwing 1023 | if( source == null ){ 1024 | throw new NullPointerException( "Source array was null." ); 1025 | } // end if 1026 | if( destination == null ){ 1027 | throw new NullPointerException( "Destination array was null." ); 1028 | } // end if 1029 | if( srcOffset < 0 || srcOffset + 3 >= source.length ){ 1030 | throw new IllegalArgumentException( 1031 | "Source array with length " + source.length + " cannot have offset of " + srcOffset + " and still process four bytes."); 1032 | } // end if 1033 | if( destOffset < 0 || destOffset +2 >= destination.length ){ 1034 | throw new IllegalArgumentException( 1035 | "Destination array with length " + destination.length + " cannot have offset of " ); 1036 | } // end if 1037 | 1038 | 1039 | byte[] DECODABET = getDecodabet( options ); 1040 | 1041 | // Example: Dk== 1042 | if( source[ srcOffset + 2] == EQUALS_SIGN ) { 1043 | // Two ways to do the same thing. Don't know which way I like best. 1044 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1045 | // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 1046 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1047 | | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); 1048 | 1049 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1050 | return 1; 1051 | } 1052 | 1053 | // Example: DkL= 1054 | else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { 1055 | // Two ways to do the same thing. Don't know which way I like best. 1056 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1057 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1058 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 1059 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1060 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1061 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); 1062 | 1063 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1064 | destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); 1065 | return 2; 1066 | } 1067 | 1068 | // Example: DkLE 1069 | else { 1070 | // Two ways to do the same thing. Don't know which way I like best. 1071 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1072 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1073 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 1074 | // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 1075 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1076 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1077 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) 1078 | | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); 1079 | 1080 | 1081 | destination[ destOffset ] = (byte)( outBuff >> 16 ); 1082 | destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); 1083 | destination[ destOffset + 2 ] = (byte)( outBuff ); 1084 | 1085 | return 3; 1086 | } 1087 | } // end decodeToBytes 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | /** 1094 | * Low-level access to decoding ASCII characters in 1095 | * the form of a byte array. Ignores GUNZIP option, if 1096 | * it's set. This is not generally a recommended method, 1097 | * although it is used internally as part of the decoding process. 1098 | * Special case: if len = 0, an empty array is returned. Still, 1099 | * if you need more speed and reduced memory footprint (and aren't 1100 | * gzipping), consider this method. 1101 | * 1102 | * @param source The Base64 encoded data 1103 | * @return decoded data 1104 | * @since 2.3.1 1105 | */ 1106 | public static byte[] decode( byte[] source ){ 1107 | byte[] decoded = null; 1108 | try { 1109 | decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); 1110 | } catch( java.io.IOException ex ) { 1111 | assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 1112 | } 1113 | return decoded; 1114 | } 1115 | 1116 | 1117 | 1118 | /** 1119 | * Low-level access to decoding ASCII characters in 1120 | * the form of a byte array. Ignores GUNZIP option, if 1121 | * it's set. This is not generally a recommended method, 1122 | * although it is used internally as part of the decoding process. 1123 | * Special case: if len = 0, an empty array is returned. Still, 1124 | * if you need more speed and reduced memory footprint (and aren't 1125 | * gzipping), consider this method. 1126 | * 1127 | * @param source The Base64 encoded data 1128 | * @param off The offset of where to begin decoding 1129 | * @param len The length of characters to decode 1130 | * @param options Can specify options such as alphabet type to use 1131 | * @return decoded data 1132 | * @throws java.io.IOException If bogus characters exist in source data 1133 | * @since 1.3 1134 | */ 1135 | public static byte[] decode( byte[] source, int off, int len, int options ) 1136 | throws java.io.IOException { 1137 | 1138 | // Lots of error checking and exception throwing 1139 | if( source == null ){ 1140 | throw new NullPointerException( "Cannot decode null source array." ); 1141 | } // end if 1142 | if( off < 0 || off + len > source.length ){ 1143 | throw new IllegalArgumentException( 1144 | "Source array with length " + source.length + " cannot have offset of " + off + " and process " + len + " bytes." ); 1145 | } // end if 1146 | 1147 | if( len == 0 ){ 1148 | return new byte[0]; 1149 | }else if( len < 4 ){ 1150 | throw new IllegalArgumentException( 1151 | "Base64-encoded string must have at least four characters, but length specified was " + len ); 1152 | } // end if 1153 | 1154 | byte[] DECODABET = getDecodabet( options ); 1155 | 1156 | int len34 = len * 3 / 4; // Estimate on array size 1157 | byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output 1158 | int outBuffPosn = 0; // Keep track of where we're writing 1159 | 1160 | byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space 1161 | int b4Posn = 0; // Keep track of four byte input buffer 1162 | int i = 0; // Source array counter 1163 | byte sbiCrop = 0; // Low seven bits (ASCII) of input 1164 | byte sbiDecode = 0; // Special value from DECODABET 1165 | 1166 | for( i = off; i < off+len; i++ ) { // Loop through source 1167 | 1168 | sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits 1169 | sbiDecode = DECODABET[ sbiCrop ]; // Special value 1170 | 1171 | // White space, Equals sign, or legit Base64 character 1172 | // Note the values such as -5 and -9 in the 1173 | // DECODABETs at the top of the file. 1174 | if( sbiDecode >= WHITE_SPACE_ENC ) { 1175 | if( sbiDecode >= EQUALS_SIGN_ENC ) { 1176 | b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace 1177 | if( b4Posn > 3 ) { // Time to decode? 1178 | outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); 1179 | b4Posn = 0; 1180 | 1181 | // If that was the equals sign, break out of 'for' loop 1182 | if( sbiCrop == EQUALS_SIGN ) { 1183 | break; 1184 | } // end if: equals sign 1185 | } // end if: quartet built 1186 | } // end if: equals sign or better 1187 | } // end if: white space, equals sign or better 1188 | else { 1189 | // There's a bad input character in the Base64 stream. 1190 | throw new java.io.IOException( 1191 | "Bad Base64 input character '" + source[i] + "' in array position " + i); 1192 | } // end else: 1193 | } // each input character 1194 | 1195 | byte[] out = new byte[ outBuffPosn ]; 1196 | System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 1197 | return out; 1198 | } // end decode 1199 | 1200 | 1201 | 1202 | 1203 | /** 1204 | * Decodes data from Base64 notation, automatically 1205 | * detecting gzip-compressed data and decompressing it. 1206 | * 1207 | * @param s the string to decode 1208 | * @return the decoded data 1209 | * @throws java.io.IOException If there is a problem 1210 | * @since 1.4 1211 | */ 1212 | public static byte[] decode( String s ) throws java.io.IOException { 1213 | return decode( s, NO_OPTIONS ); 1214 | } 1215 | 1216 | 1217 | 1218 | /** 1219 | * Decodes data from Base64 notation, automatically 1220 | * detecting gzip-compressed data and decompressing it. 1221 | * 1222 | * @param s the string to decode 1223 | * @param options encode options such as URL_SAFE 1224 | * @return the decoded data 1225 | * @throws java.io.IOException if there is an error 1226 | * @throws NullPointerException if s is null 1227 | * @since 1.4 1228 | */ 1229 | public static byte[] decode( String s, int options ) throws java.io.IOException { 1230 | 1231 | if( s == null ){ 1232 | throw new NullPointerException( "Input string was null." ); 1233 | } // end if 1234 | 1235 | byte[] bytes; 1236 | try { 1237 | bytes = s.getBytes( PREFERRED_ENCODING ); 1238 | } // end try 1239 | catch( java.io.UnsupportedEncodingException uee ) { 1240 | bytes = s.getBytes(); 1241 | } // end catch 1242 | // 1243 | 1244 | // Decode 1245 | bytes = decode( bytes, 0, bytes.length, options ); 1246 | 1247 | // Check to see if it's gzip-compressed 1248 | // GZIP Magic Two-Byte Number: 0x8b1f (35615) 1249 | boolean dontGunzip = (options & DONT_GUNZIP) != 0; 1250 | if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { 1251 | 1252 | int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); 1253 | if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { 1254 | java.io.ByteArrayInputStream bais = null; 1255 | java.util.zip.GZIPInputStream gzis = null; 1256 | java.io.ByteArrayOutputStream baos = null; 1257 | byte[] buffer = new byte[2048]; 1258 | int length = 0; 1259 | 1260 | try { 1261 | baos = new java.io.ByteArrayOutputStream(); 1262 | bais = new java.io.ByteArrayInputStream( bytes ); 1263 | gzis = new java.util.zip.GZIPInputStream( bais ); 1264 | 1265 | while( ( length = gzis.read( buffer ) ) >= 0 ) { 1266 | baos.write(buffer,0,length); 1267 | } // end while: reading input 1268 | 1269 | // No error? Get new bytes. 1270 | bytes = baos.toByteArray(); 1271 | 1272 | } // end try 1273 | catch( java.io.IOException e ) { 1274 | e.printStackTrace(); 1275 | // Just return originally-decoded bytes 1276 | } // end catch 1277 | finally { 1278 | try{ baos.close(); } catch( Exception e ){} 1279 | try{ gzis.close(); } catch( Exception e ){} 1280 | try{ bais.close(); } catch( Exception e ){} 1281 | } // end finally 1282 | 1283 | } // end if: gzipped 1284 | } // end if: bytes.length >= 2 1285 | 1286 | return bytes; 1287 | } // end decode 1288 | 1289 | /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 1290 | 1291 | 1292 | 1293 | /** 1294 | * A {@link Base64.InputStream} will read data from another 1295 | * java.io.InputStream, given in the constructor, 1296 | * and encode/decode to/from Base64 notation on the fly. 1297 | * 1298 | * @see Base64 1299 | * @since 1.3 1300 | */ 1301 | public static class InputStream extends java.io.FilterInputStream { 1302 | 1303 | private boolean encode; // Encoding or decoding 1304 | private int position; // Current position in the buffer 1305 | private byte[] buffer; // Small buffer holding converted data 1306 | private int bufferLength; // Length of buffer (3 or 4) 1307 | private int numSigBytes; // Number of meaningful bytes in the buffer 1308 | private int lineLength; 1309 | private boolean breakLines; // Break lines at less than 80 characters 1310 | private int options; // Record options used to create the stream. 1311 | private byte[] decodabet; // Local copies to avoid extra method calls 1312 | 1313 | 1314 | /** 1315 | * Constructs a {@link Base64.InputStream} in DECODE mode. 1316 | * 1317 | * @param in the java.io.InputStream from which to read data. 1318 | * @since 1.3 1319 | */ 1320 | public InputStream( java.io.InputStream in ) { 1321 | this( in, DECODE ); 1322 | } // end constructor 1323 | 1324 | 1325 | /** 1326 | * Constructs a {@link Base64.InputStream} in 1327 | * either ENCODE or DECODE mode. 1328 | *1329 | * Valid options:
1330 | * ENCODE or DECODE: Encode or Decode as data is read. 1331 | * DO_BREAK_LINES: break lines at 76 characters 1332 | * (only meaningful when encoding) 1333 | *1334 | *
1335 | * Example: new Base64.InputStream( in, Base64.DECODE )
1336 | *
1337 | *
1338 | * @param in the java.io.InputStream from which to read data.
1339 | * @param options Specified options
1340 | * @see Base64#ENCODE
1341 | * @see Base64#DECODE
1342 | * @see Base64#DO_BREAK_LINES
1343 | * @since 2.0
1344 | */
1345 | public InputStream( java.io.InputStream in, int options ) {
1346 |
1347 | super( in );
1348 | this.options = options; // Record for later
1349 | this.breakLines = (options & DO_BREAK_LINES) > 0;
1350 | this.encode = (options & ENCODE) > 0;
1351 | this.bufferLength = encode ? 4 : 3;
1352 | this.buffer = new byte[ bufferLength ];
1353 | this.position = -1;
1354 | this.lineLength = 0;
1355 | this.decodabet = getDecodabet(options);
1356 | } // end constructor
1357 |
1358 | /**
1359 | * Reads enough of the input stream to convert
1360 | * to/from Base64 and returns the next byte.
1361 | *
1362 | * @return next byte
1363 | * @since 1.3
1364 | */
1365 | public int read() throws java.io.IOException {
1366 |
1367 | // Do we need to get data?
1368 | if( position < 0 ) {
1369 | if( encode ) {
1370 | byte[] b3 = new byte[3];
1371 | int numBinaryBytes = 0;
1372 | for( int i = 0; i < 3; i++ ) {
1373 | int b = in.read();
1374 |
1375 | // If end of stream, b is -1.
1376 | if( b >= 0 ) {
1377 | b3[i] = (byte)b;
1378 | numBinaryBytes++;
1379 | } else {
1380 | break; // out of for loop
1381 | } // end else: end of stream
1382 |
1383 | } // end for: each needed input byte
1384 |
1385 | if( numBinaryBytes > 0 ) {
1386 | encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1387 | position = 0;
1388 | numSigBytes = 4;
1389 | } // end if: got data
1390 | else {
1391 | return -1; // Must be end of stream
1392 | } // end else
1393 | } // end if: encoding
1394 |
1395 | // Else decoding
1396 | else {
1397 | byte[] b4 = new byte[4];
1398 | int i = 0;
1399 | for( i = 0; i < 4; i++ ) {
1400 | // Read four "meaningful" bytes:
1401 | int b = 0;
1402 | do{ b = in.read(); }
1403 | while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1404 |
1405 | if( b < 0 ) {
1406 | break; // Reads a -1 if end of stream
1407 | } // end if: end of stream
1408 |
1409 | b4[i] = (byte)b;
1410 | } // end for: each needed input byte
1411 |
1412 | if( i == 4 ) {
1413 | numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1414 | position = 0;
1415 | } // end if: got four characters
1416 | else if( i == 0 ){
1417 | return -1;
1418 | } // end else if: also padded correctly
1419 | else {
1420 | // Must have broken out from above.
1421 | throw new java.io.IOException( "Improperly padded Base64 input." );
1422 | } // end
1423 |
1424 | } // end else: decode
1425 | } // end else: get data
1426 |
1427 | // Got data?
1428 | if( position >= 0 ) {
1429 | // End of relevant data?
1430 | if( /*!encode &&*/ position >= numSigBytes ){
1431 | return -1;
1432 | } // end if: got data
1433 |
1434 | if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
1435 | lineLength = 0;
1436 | return '\n';
1437 | } // end if
1438 | else {
1439 | lineLength++; // This isn't important when decoding
1440 | // but throwing an extra "if" seems
1441 | // just as wasteful.
1442 |
1443 | int b = buffer[ position++ ];
1444 |
1445 | if( position >= bufferLength ) {
1446 | position = -1;
1447 | } // end if: end
1448 |
1449 | return b & 0xFF; // This is how you "cast" a byte that's
1450 | // intended to be unsigned.
1451 | } // end else
1452 | } // end if: position >= 0
1453 |
1454 | // Else error
1455 | else {
1456 | throw new java.io.IOException( "Error in Base64 code reading stream." );
1457 | } // end else
1458 | } // end read
1459 |
1460 |
1461 | /**
1462 | * Calls {@link #read()} repeatedly until the end of stream
1463 | * is reached or len bytes are read.
1464 | * Returns number of bytes read into array or -1 if
1465 | * end of stream is encountered.
1466 | *
1467 | * @param dest array to hold values
1468 | * @param off offset for array
1469 | * @param len max number of bytes to read into array
1470 | * @return bytes read into array or -1 if end of stream is encountered.
1471 | * @since 1.3
1472 | */
1473 | public int read( byte[] dest, int off, int len )
1474 | throws java.io.IOException {
1475 | int i;
1476 | int b;
1477 | for( i = 0; i < len; i++ ) {
1478 | b = read();
1479 |
1480 | if( b >= 0 ) {
1481 | dest[off + i] = (byte) b;
1482 | }
1483 | else if( i == 0 ) {
1484 | return -1;
1485 | }
1486 | else {
1487 | break; // Out of 'for' loop
1488 | } // Out of 'for' loop
1489 | } // end for: each byte read
1490 | return i;
1491 | } // end read
1492 |
1493 | } // end inner class InputStream
1494 |
1495 |
1496 |
1497 |
1498 |
1499 |
1500 | /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1501 |
1502 |
1503 |
1504 | /**
1505 | * A {@link Base64.OutputStream} will write data to another
1506 | * java.io.OutputStream, given in the constructor,
1507 | * and encode/decode to/from Base64 notation on the fly.
1508 | *
1509 | * @see Base64
1510 | * @since 1.3
1511 | */
1512 | public static class OutputStream extends java.io.FilterOutputStream {
1513 |
1514 | private boolean encode;
1515 | private int position;
1516 | private byte[] buffer;
1517 | private int bufferLength;
1518 | private int lineLength;
1519 | private boolean breakLines;
1520 | private byte[] b4; // Scratch used in a few places
1521 | private boolean suspendEncoding;
1522 | private int options; // Record for later
1523 | private byte[] decodabet; // Local copies to avoid extra method calls
1524 |
1525 | /**
1526 | * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1527 | *
1528 | * @param out the java.io.OutputStream to which data will be written.
1529 | * @since 1.3
1530 | */
1531 | public OutputStream( java.io.OutputStream out ) {
1532 | this( out, ENCODE );
1533 | } // end constructor
1534 |
1535 |
1536 | /**
1537 | * Constructs a {@link Base64.OutputStream} in
1538 | * either ENCODE or DECODE mode.
1539 | *
1540 | * Valid options:
1541 | * ENCODE or DECODE: Encode or Decode as data is read. 1542 | * DO_BREAK_LINES: don't break lines at 76 characters 1543 | * (only meaningful when encoding) 1544 | *1545 | *
1546 | * Example: new Base64.OutputStream( out, Base64.ENCODE )
1547 | *
1548 | * @param out the java.io.OutputStream to which data will be written.
1549 | * @param options Specified options.
1550 | * @see Base64#ENCODE
1551 | * @see Base64#DECODE
1552 | * @see Base64#DO_BREAK_LINES
1553 | * @since 1.3
1554 | */
1555 | public OutputStream( java.io.OutputStream out, int options ) {
1556 | super( out );
1557 | this.breakLines = (options & DO_BREAK_LINES) != 0;
1558 | this.encode = (options & ENCODE) != 0;
1559 | this.bufferLength = encode ? 3 : 4;
1560 | this.buffer = new byte[ bufferLength ];
1561 | this.position = 0;
1562 | this.lineLength = 0;
1563 | this.suspendEncoding = false;
1564 | this.b4 = new byte[4];
1565 | this.options = options;
1566 | this.decodabet = getDecodabet(options);
1567 | } // end constructor
1568 |
1569 |
1570 | /**
1571 | * Writes the byte to the output stream after
1572 | * converting to/from Base64 notation.
1573 | * When encoding, bytes are buffered three
1574 | * at a time before the output stream actually
1575 | * gets a write() call.
1576 | * When decoding, bytes are buffered four
1577 | * at a time.
1578 | *
1579 | * @param theByte the byte to write
1580 | * @since 1.3
1581 | */
1582 | public void write(int theByte)
1583 | throws java.io.IOException {
1584 | // Encoding suspended?
1585 | if( suspendEncoding ) {
1586 | this.out.write( theByte );
1587 | return;
1588 | } // end if: supsended
1589 |
1590 | // Encode?
1591 | if( encode ) {
1592 | buffer[ position++ ] = (byte)theByte;
1593 | if( position >= bufferLength ) { // Enough to encode.
1594 |
1595 | this.out.write( encode3to4( b4, buffer, bufferLength, options ) );
1596 |
1597 | lineLength += 4;
1598 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
1599 | this.out.write( NEW_LINE );
1600 | lineLength = 0;
1601 | } // end if: end of line
1602 |
1603 | position = 0;
1604 | } // end if: enough to output
1605 | } // end if: encoding
1606 |
1607 | // Else, Decoding
1608 | else {
1609 | // Meaningful Base64 character?
1610 | if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) {
1611 | buffer[ position++ ] = (byte)theByte;
1612 | if( position >= bufferLength ) { // Enough to output.
1613 |
1614 | int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1615 | out.write( b4, 0, len );
1616 | position = 0;
1617 | } // end if: enough to output
1618 | } // end if: meaningful base64 character
1619 | else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
1620 | throw new java.io.IOException( "Invalid character in Base64 data." );
1621 | } // end else: not white space either
1622 | } // end else: decoding
1623 | } // end write
1624 |
1625 |
1626 |
1627 | /**
1628 | * Calls {@link #write(int)} repeatedly until len
1629 | * bytes are written.
1630 | *
1631 | * @param theBytes array from which to read bytes
1632 | * @param off offset for array
1633 | * @param len max number of bytes to read into array
1634 | * @since 1.3
1635 | */
1636 | public void write( byte[] theBytes, int off, int len )
1637 | throws java.io.IOException {
1638 | // Encoding suspended?
1639 | if( suspendEncoding ) {
1640 | this.out.write( theBytes, off, len );
1641 | return;
1642 | } // end if: supsended
1643 |
1644 | for( int i = 0; i < len; i++ ) {
1645 | write( theBytes[ off + i ] );
1646 | } // end for: each byte written
1647 |
1648 | } // end write
1649 |
1650 |
1651 |
1652 | /**
1653 | * Method added by PHIL. [Thanks, PHIL. -Rob]
1654 | * This pads the buffer without closing the stream.
1655 | * @throws java.io.IOException if there's an error.
1656 | */
1657 | public void flushBase64() throws java.io.IOException {
1658 | if( position > 0 ) {
1659 | if( encode ) {
1660 | out.write( encode3to4( b4, buffer, position, options ) );
1661 | position = 0;
1662 | } // end if: encoding
1663 | else {
1664 | throw new java.io.IOException( "Base64 input not properly padded." );
1665 | } // end else: decoding
1666 | } // end if: buffer partially full
1667 |
1668 | } // end flush
1669 |
1670 |
1671 | /**
1672 | * Flushes and closes (I think, in the superclass) the stream.
1673 | *
1674 | * @since 1.3
1675 | */
1676 | public void close() throws java.io.IOException {
1677 | // 1. Ensure that pending characters are written
1678 | flushBase64();
1679 |
1680 | // 2. Actually close the stream
1681 | // Base class both flushes and closes.
1682 | super.close();
1683 |
1684 | buffer = null;
1685 | out = null;
1686 | } // end close
1687 |
1688 |
1689 |
1690 | /**
1691 | * Suspends encoding of the stream.
1692 | * May be helpful if you need to embed a piece of
1693 | * base64-encoded data in a stream.
1694 | *
1695 | * @throws java.io.IOException if there's an error flushing
1696 | * @since 1.5.1
1697 | */
1698 | public void suspendEncoding() throws java.io.IOException {
1699 | flushBase64();
1700 | this.suspendEncoding = true;
1701 | } // end suspendEncoding
1702 |
1703 |
1704 | /**
1705 | * Resumes encoding of the stream.
1706 | * May be helpful if you need to embed a piece of
1707 | * base64-encoded data in a stream.
1708 | *
1709 | * @since 1.5.1
1710 | */
1711 | public void resumeEncoding() {
1712 | this.suspendEncoding = false;
1713 | } // end resumeEncoding
1714 |
1715 |
1716 |
1717 | } // end inner class OutputStream
1718 |
1719 |
1720 | } // end class Base64
1721 |
--------------------------------------------------------------------------------
/src/net/nczonline/web/datauri/DataURI.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 Nicholas C. Zakas. All rights reserved.
3 | * http://www.nczonline.net/
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | */
23 | package net.nczonline.web.datauri;
24 |
25 | import jargs.gnu.CmdLineParser;
26 | import java.io.File;
27 | import java.io.FileOutputStream;
28 | import java.io.IOException;
29 | import java.io.OutputStreamWriter;
30 | import java.io.Writer;
31 | import java.net.URL;
32 |
33 |
34 | public class DataURI {
35 |
36 |
37 | /**
38 | * @param args the command line arguments
39 | */
40 | public static void main(String[] args) {
41 |
42 | //default settings
43 | boolean verbose = false;
44 | String charset = null;
45 | String outputFilename = null;
46 | Writer out = null;
47 | String mimeType = null;
48 |
49 | //initialize command line parser
50 | CmdLineParser parser = new CmdLineParser();
51 | CmdLineParser.Option verboseOpt = parser.addBooleanOption('v', "verbose");
52 | CmdLineParser.Option mimeTypeOpt = parser.addStringOption('m', "mime");
53 | CmdLineParser.Option helpOpt = parser.addBooleanOption('h', "help");
54 | CmdLineParser.Option charsetOpt = parser.addStringOption("charset");
55 | CmdLineParser.Option outputFilenameOpt = parser.addStringOption('o', "output");
56 |
57 | try {
58 |
59 | //parse the arguments
60 | parser.parse(args);
61 |
62 | //figure out if the help option has been executed
63 | Boolean help = (Boolean) parser.getOptionValue(helpOpt);
64 | if (help != null && help.booleanValue()) {
65 | usage();
66 | System.exit(0);
67 | }
68 |
69 | //determine boolean options
70 | verbose = parser.getOptionValue(verboseOpt) != null;
71 |
72 | //check for charset
73 | charset = (String) parser.getOptionValue(charsetOpt);
74 |
75 | //check for MIME type
76 | mimeType = (String) parser.getOptionValue(mimeTypeOpt);
77 |
78 | //get the file arguments
79 | String[] fileArgs = parser.getRemainingArgs();
80 |
81 | //need to have at least one file
82 | if (fileArgs.length == 0){
83 | System.err.println("[ERROR] No files specified.");
84 | System.exit(1);
85 | }
86 |
87 | //only the first filename is used
88 | String inputFilename = fileArgs[0];
89 |
90 | //get output filename
91 | outputFilename = (String) parser.getOptionValue(outputFilenameOpt);
92 |
93 | if (outputFilename == null) {
94 | if (verbose){
95 | System.err.println("[INFO] No output file specified, defaulting to stdout.");
96 | }
97 |
98 | out = new OutputStreamWriter(System.out);
99 | } else {
100 | if (verbose){
101 | System.err.println("[INFO] Output file is '" + (new File(outputFilename)).getAbsolutePath() + "'");
102 | }
103 | out = new OutputStreamWriter(new FileOutputStream(outputFilename), "UTF-8");
104 | }
105 |
106 | //set verbose option
107 | DataURIGenerator.setVerbose(verbose);
108 |
109 | //determine if the filename is a local file or a URL
110 | if (inputFilename.startsWith("http://")){
111 | DataURIGenerator.generate(new URL(inputFilename), out, mimeType);
112 | } else {
113 | DataURIGenerator.generate(new File(inputFilename), out, mimeType);
114 | }
115 |
116 | } catch (CmdLineParser.OptionException e) {
117 | usage();
118 | System.exit(1);
119 | } catch (Exception e) {
120 | e.printStackTrace();
121 | System.exit(1);
122 | } finally {
123 | if (out != null) {
124 | try {
125 | out.close();
126 | } catch (IOException e) {
127 | e.printStackTrace();
128 | }
129 | }
130 | }
131 |
132 | }
133 |
134 | /**
135 | * Outputs help information to the console.
136 | */
137 | private static void usage() {
138 | System.out.println(
139 | "\nUsage: java -jar datauri-x.y.z.jar [options] [input file]\n\n"
140 |
141 | + "Global Options\n"
142 | + " -h, --help Displays this information.\n"
143 | + " --charset