Jeśli masz UTF8, użyj tego (faktycznie działa ze źródłem SVG), na przykład:
btoa(unescape(encodeURIComponent(str)))
przykład:
var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(markup)));
var img = new Image(1, 1); // width, height values are optional params
img.src = imgsrc;
Jeśli chcesz zdekodować ten base64, użyj tego:
var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);
Przykład:
var str = "äöüÄÖÜçéèñ";
var b64 = window.btoa(unescape(encodeURIComponent(str)))
console.log(b64);
var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);
Uwaga: jeśli chcesz, aby to działało w mobilnym safari, może być konieczne usunięcie całego odstępu z danych base64 ...
function b64_to_utf8( str ) {
str = str.replace(/\s/g, '');
return decodeURIComponent(escape(window.atob( str )));
}
Aktualizacja 2017
Ten problem znowu mnie niepokoi.
Prosta prawda jest taka, że atob tak naprawdę nie obsługuje łańcuchów UTF8 - to tylko ASCII.
Nie użyłbym też oprogramowania typu bloatware, takiego jak js-base64.
Ale webtoolkit ma małą, ładną i łatwą w utrzymaniu implementację:
/**
*
* Base64 encode / decode
* http://www.webtoolkit.info
*
**/
var Base64 = {
// private property
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
// public method for encoding
, encode: function (input)
{
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length)
{
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2))
{
enc3 = enc4 = 64;
}
else if (isNaN(chr3))
{
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
} // Whend
return output;
} // End Function encode
// public method for decoding
,decode: function (input)
{
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length)
{
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64)
{
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64)
{
output = output + String.fromCharCode(chr3);
}
} // Whend
output = Base64._utf8_decode(output);
return output;
} // End Function decode
// private method for UTF-8 encoding
,_utf8_encode: function (string)
{
var utftext = "";
string = string.replace(/\r\n/g, "\n");
for (var n = 0; n < string.length; n++)
{
var c = string.charCodeAt(n);
if (c < 128)
{
utftext += String.fromCharCode(c);
}
else if ((c > 127) && (c < 2048))
{
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else
{
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
} // Next n
return utftext;
} // End Function _utf8_encode
// private method for UTF-8 decoding
,_utf8_decode: function (utftext)
{
var string = "";
var i = 0;
var c, c1, c2, c3;
c = c1 = c2 = 0;
while (i < utftext.length)
{
c = utftext.charCodeAt(i);
if (c < 128)
{
string += String.fromCharCode(c);
i++;
}
else if ((c > 191) && (c < 224))
{
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else
{
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
} // Whend
return string;
} // End Function _utf8_decode
}
https://www.fileformat.info/info/unicode/utf8.htm
Dla dowolnego znaku równego lub mniejszego niż 127 (szesnastkowo 0x7F) reprezentacja UTF-8 to jeden bajt. To tylko najniższe 7 bitów pełnej wartości Unicode. Jest to również to samo, co wartość ASCII.
W przypadku znaków równych 2047 lub mniejszych (szesnastkowo 0x07FF) reprezentacja UTF-8 jest rozłożona na dwa bajty. Pierwszy bajt będzie miał ustawione dwa wysokie bity, a trzeci bit wyczyszczony (tj. 0xC2 do 0xDF). Drugi bajt będzie miał ustawiony górny bit, a drugi bit wyczyszczony (tj. 0x80 do 0xBF).
Dla wszystkich znaków równych lub większych niż 2048, ale mniejszych niż 65535 (0xFFFF), reprezentacja UTF-8 jest rozłożona na trzy bajty.
escape
konwertuje ciąg w taki, który zawiera tylko prawidłowe znaki adresu URL. To zapobiega błędom.escape
iunescape
zostały uznane za przestarzałe w JavaScript 1.5 i zamiast tego należy użyć odpowiednioencodeURIComponent
lubdecodeURIComponent
. Używasz jednocześnie przestarzałych i nowych funkcji. Czemu? Zobacz: w3schools.com/jsref/jsref_escape.aspKorzystanie
btoa
zunescape
iencodeURIComponent
nie działa dla mnie. Zastąpienie wszystkich znaków specjalnych encjami XML / HTML, a następnie konwersja do reprezentacji base64 było jedynym sposobem rozwiązania tego problemu. Jakiś kod:źródło
Blob
obiektu do obsługi konwersji.Blob
może obsługiwać dowolne dane binarne.Zamiast tego użyj biblioteki
Nie musimy odkrywać koła na nowo. Wystarczy skorzystać z biblioteki, aby zaoszczędzić czas i ból głowy.
js-base64
https://github.com/dankogai/js-base64 jest dobry i potwierdzam, że bardzo dobrze obsługuje Unicode.
źródło
Pomyślałem, że powinienem podzielić się tym, jak faktycznie rozwiązałem problem i dlaczego uważam, że jest to właściwe rozwiązanie (pod warunkiem, że nie zoptymalizujesz pod starą przeglądarkę).
Konwertowanie danych na dataURL (
data: ...
)Umożliwienie użytkownikowi zapisywania danych
Oprócz oczywistego rozwiązania - otwierając nowe okno z Twoim dataURL jako URL możesz zrobić jeszcze dwie rzeczy.
1. Użyj fileSaver.js
Funkcja oszczędzania plików może tworzyć rzeczywiste okno dialogowe Zapisz plik ze wstępnie zdefiniowaną nazwą pliku. Może również powrócić do normalnego podejścia dataURL.
2. Użyj (eksperymentalnie)
URL.createObjectURL
Jest to świetne rozwiązanie do ponownego wykorzystywania danych zakodowanych w standardzie base64. Tworzy krótki adres URL dla Twojego dataURL:
Nie zapomnij użyć adresu URL zawierającego początkowy
blob
prefiks. Użyłemdocument.body
ponownie:Możesz użyć tego krótkiego adresu URL jako lokalizacji docelowej,
<script>
źródłowej lub<a>
href AJAX . Jesteś jednak odpowiedzialny za zniszczenie adresu URL:źródło
Jako uzupełnienie odpowiedzi Stefana Steigera: (bo nie wygląda to ładnie jako komentarz)
Rozszerzający prototyp String:
Stosowanie:
UWAGA:
Jak stwierdzono w komentarzach, używanie
unescape
nie jest zalecane, ponieważ może zostać usunięte w przyszłości:źródło
unescape
wkrótce zostanie wycofany zgodnie z MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...btoa () obsługuje tylko znaki od String.fromCodePoint (0) do String.fromCodePoint (255). W przypadku znaków Base64 z punktem kodowym 256 lub wyższym należy je zakodować / zdekodować przed i po.
I w tym miejscu staje się trudne ...
Każdy możliwy znak jest umieszczony w tabeli Unicode. Tablica Unicode jest podzielona na różne płaszczyzny (języki, symbole matematyczne i tak dalej ...). Każdy znak w samolocie ma unikalny numer punktu kodowego. Teoretycznie liczba może stać się dowolnie duża.
Komputer przechowuje dane w bajtach (8 bitów, szesnastkowo 0x00 - 0xff, dwójkowo 00000000 - 11111111, dziesiętnie 0 - 255). Ten zakres zwykle służy do zapisywania podstawowych znaków (zakres Latin1).
Dla znaków o wyższym punkcie kodowym niż 255 istnieją różne kodowania. JavaScript używa 16 bitów na znak (UTF-16), ciąg o nazwie DOMString. Unicode może obsługiwać punkty kodowe do 0x10fffff. Oznacza to, że musi istnieć metoda przechowywania kilku bitów w kilku komórkach dalej.
String.fromCodePoint(0x10000).length == 2
UTF-16 używa zastępczych par do przechowywania 20 bitów w dwóch 16-bitowych komórkach. Pierwszy wyższy surogat zaczyna się od 110110xxxxxxxxxx , dolny drugi od 110111xxxxxxxxxx . Unicode zarezerwował w tym celu własne samoloty: https://unicode-table.com/de/#high-surrogates
Aby przechowywać znaki w bajtach (zakres Latin1), standardowe procedury używają UTF-8 .
Przykro mi to mówić, ale myślę, że nie ma innego sposobu na zaimplementowanie tej funkcji.
Jak tego użyć:
decodeBase64(encodeBase64("\u{1F604}"))
demo: https://jsfiddle.net/qrLadeb8/
źródło
stringToUTF8
iutf8ToString
chociażWłaśnie sam napotkałem ten problem.
Najpierw nieznacznie zmodyfikuj kod:
Następnie użyj swojego ulubionego inspektora sieci, umieść punkt przerwania w linii kodu, który przypisuje this.loader.src, a następnie wykonaj ten kod:
W zależności od aplikacji, zastępowanie znaków spoza zakresu może, ale nie musi, działać, ponieważ będziesz modyfikować dane. Zobacz uwagę na MDN o znakach Unicode w metodzie btoa:
https://developer.mozilla.org/en-US/docs/Web/API/window.btoa
źródło