2013-03-01 15 views
8

Próbuję odczytać plik binarny z systemu plików, a następnie kodować go base64 w JavaScript. Używam FileReader API do odczytu danych i kodera base64 znalezionego here.Odczyt i kodowanie base64 pliku binarnego

Kod, który mam wydaje się być blisko pracy, problem polega na tym, że wygenerowane dane base64 są nieprawidłowe. Oto co mam do tej pory:

function saveResource() { 
    var file = $(".resourceFile")[0].files[0]; 

    var reader = new FileReader(); 
    reader.onload = function(evt) { 
     var fileData = evt.target.result; 
     var bytes = new Uint8Array(fileData); 
     var binaryText = ''; 

     for (var index = 0; index < bytes.byteLength; index++) { 
      binaryText += String.fromCharCode(bytes[index]); 
     } 

     console.log(Base64.encode(binaryText)); 

    }; 
    reader.readAsArrayBuffer(file); 
}; 

Oto plik mam testowanie z (jest to 100x100 niebieski kwadrat):

enter image description here

Według online base64 decoder/encoder ten plik powinien kodować do:

/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4 + Tl5ufo6erx8vP09fb3 + PN6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3 + PN6/9oADAMBAAIRAxEAPwDxyiiiv3E8wKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA // Z

... ale zamiast tego, co dostaję z JavaScript jest:

W7/DmMO/w6AAEEpGSUYAAQIAAAEAAQAAw7/DmwBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDLDv8ObAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMsO/w4AAEQgAZABkAwEiAAIRAQMRAcO/w4QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoLw7/DhADCtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDLCgcKRwqEII0LCscOBFVLDkcOwJDNicsKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5esKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDocOiw6PDpMOlw6bDp8Oow6nDqsOxw7LDs8O0w7XD tsO3w7jDucO6w7/DhAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgvDv8OEAMK1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIywoEIFELCkcKhwrHDgQkjM1LDsBVicsORChYkNMOhJcOxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXrCgsKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDosOjw6TDpcOmw6fDqMOpw6rDssOzw7TDtcO2w7fDuMO5w7rDv8OaAAwDAQACEQMRAD8Aw7HDiijCosK/cTzDgMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooA8O/w5k =

Gdybym musiał zaryzykować przypuszczenie, powiedziałbym, że problem ma coś wspólnego z niedrukowalnymi znakami w danych binarnych (jeśli koduję dokument w postaci zwykłego tekstu, to działa dobrze). Ale jakie jest najlepsze podejście do rozwiązania tego problemu?

Edit

Wygląda na to, może to być problem z biblioteką base64 samego (a jeśli nie, wtedy z przebiegu Uint8Array jest bez opakowania na sznurku do wywołania biblioteki). Jeśli użyję zamiast tego funkcji przeglądarki btoa() i przekazuję ją bezpośrednio, to działa. Szkoda, że ​​ta funkcja nie istnieje we wszystkich przeglądarkach.

Odpowiedz

8

I Google na ratunek. Znalazłem następujący kod, który pobiera dane wejściowe jako zwykłą tablicę "bajtów" (liczby od 0 do 255 włącznie, działa również poprawnie, jeśli Uint8Array jest przekazywany bezpośrednio) i dodaje ją do biblioteki, której używałem :

//note: it is assumed that the Base64 object has already been defined 
//License: Apache 2.0 
Base64.byteToCharMap_ = null; 
Base64.charToByteMap_ = null; 
Base64.byteToCharMapWebSafe_ = null; 
Base64.charToByteMapWebSafe_ = null; 
Base64.ENCODED_VALS_BASE = 
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 
    'abcdefghijklmnopqrstuvwxyz' + 
    ''; 

/** 
* Our default alphabet. Value 64 (=) is special; it means "nothing." 
* @type {string} 
*/ 
Base64.ENCODED_VALS = Base64.ENCODED_VALS_BASE + '+/='; 
Base64.ENCODED_VALS_WEBSAFE = Base64.ENCODED_VALS_BASE + '-_.'; 

/** 
* Base64-encode an array of bytes. 
* 
* @param {Array.<number>|Uint8Array} input An array of bytes (numbers with 
*  value in [0, 255]) to encode. 
* @param {boolean=} opt_webSafe Boolean indicating we should use the 
*  alternative alphabet. 
* @return {string} The base64 encoded string. 
*/ 
Base64.encodeByteArray = function(input, opt_webSafe) { 
    Base64.init_(); 

    var byteToCharMap = opt_webSafe ? 
         Base64.byteToCharMapWebSafe_ : 
         Base64.byteToCharMap_; 

    var output = []; 

    for (var i = 0; i < input.length; i += 3) { 
    var byte1 = input[i]; 
    var haveByte2 = i + 1 < input.length; 
    var byte2 = haveByte2 ? input[i + 1] : 0; 
    var haveByte3 = i + 2 < input.length; 
    var byte3 = haveByte3 ? input[i + 2] : 0; 

    var outByte1 = byte1 >> 2; 
    var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); 
    var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); 
    var outByte4 = byte3 & 0x3F; 

    if (!haveByte3) { 
     outByte4 = 64; 

     if (!haveByte2) { 
     outByte3 = 64; 
     } 
    } 

    output.push(byteToCharMap[outByte1], 
       byteToCharMap[outByte2], 
       byteToCharMap[outByte3], 
       byteToCharMap[outByte4]); 
    } 

    return output.join(''); 
}; 

/** 
* Lazy static initialization function. Called before 
* accessing any of the static map variables. 
* @private 
*/ 
Base64.init_ = function() { 
    if (!Base64.byteToCharMap_) { 
    Base64.byteToCharMap_ = {}; 
    Base64.charToByteMap_ = {}; 
    Base64.byteToCharMapWebSafe_ = {}; 
    Base64.charToByteMapWebSafe_ = {}; 

    // We want quick mappings back and forth, so we precompute two maps. 
    for (var i = 0; i < Base64.ENCODED_VALS.length; i++) { 
     Base64.byteToCharMap_[i] = 
      Base64.ENCODED_VALS.charAt(i); 
     Base64.charToByteMap_[Base64.byteToCharMap_[i]] = i; 
     Base64.byteToCharMapWebSafe_[i] = 
      Base64.ENCODED_VALS_WEBSAFE.charAt(i); 
     Base64.charToByteMapWebSafe_[ 
      Base64.byteToCharMapWebSafe_[i]] = i; 
    } 
    } 
}; 

pełny kod dla biblioteki zawierającej powyższe funkcje jest available here, ale w niezmodyfikowanej postaci wydaje się zależeć od ilości innych bibliotekach. Ta nieco zmodyfikowana wersja powinna działać u każdego, kto potrzebuje szybkiej naprawy tego problemu.

+0

To jest dokładnie to, czego szukam - przypadkiem nadania część dekodowania? – Graham

+1

Nie mam własnej wersji części dekodującej, ponieważ nie potrzebowałem jej do moich celów (dekodowanie odbywa się po stronie serwera w moim przypadku). Jednak oryginalny kod źródłowy z Google jest dostępny tutaj: http://docs.closure-library.googlecode.com/git/closure_goog_crypt_base64.js.source.html – aroth

+0

Tak, spojrzał na to, ale ma zależności, które usunięty w twoim kodowanym kawałku, więc liczyłem na coś podobnego, ale dzięki za odpowiedź. – Graham

2

Traktuj plik binarny jako arraybuffer, niezależnie od kodowania znaków. Twój niebieski kwadrat (.jpg) ma 361 natywnych bajtów, oznacza oktety od 0..255 (dziesiętnie) i nie są one znakami!

Oznacza to: Użyj ArrayBuffer do kodowania tego do Base64 przy użyciu dobrze znanego algorytmu base64.

z Perl z powrotem do pochodzenia, pokazuje niebieski kwadrat jak wyżej:

my $fh = IO::File->new; 
$fh->open("d:/tmp/x.jpg", O_BINARY|O_CREAT|O_RDWR|O_TRUNC) or die $!; 

$fh->print(decode_base64("/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBD 
AQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAFQABAQAA 
AAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAUH/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMR 
AD8AjgDcUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//2Q== 
")); 


$fh->close;