使用JavaScript判断字符串是否为base64

58

我正在使用window.atob('string')函数将一个字符串从Base64解码为普通字符串。现在,我想知道是否有任何方法可以检查'string'是否为有效的Base64格式。如果该字符串不是Base64格式,我希望得到通知,以便我可以执行其他操作。


你的问题是如何确定一个字符串是否为有效的base64,还是你正在查看一个编码了信息的base64字符串?这是一个微妙的区别 - 对于前者,在下面有一些很好的答案,对于后者,没有确定性的答案(就像问声音是音乐还是语言一样)。因此,我建议在你的问题标题中用“valid”替换“in”。 - Philzen
13个回答

71

@anders-marzi-tornblad的答案的基础上,使用正则表达式来进行base64有效性的简单真/假测试非常容易,如下所示:

var base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;

base64regex.test("SomeStringObviouslyNotBase64Encoded...");             // FALSE
base64regex.test("U29tZVN0cmluZ09idmlvdXNseU5vdEJhc2U2NEVuY29kZWQ=");   // TRUE

2021年更新

  • 根据下面的评论,发现基于正则表达式的解决方案比简单地尝试`atob`提供了更准确的检查,因为后者不检查`=`填充。根据RFC4648,`=`填充只能在进行base16编码或数据长度已知的情况下被忽略。
  • 基于正则表达式的解决方案似乎也是最快的,如提示所示,由kai提供。由于jsperf目前似乎有问题,我在jsbench上进行了新的测试,证实了这一点。

1
base64regex.test("mindmaps"); // 预期返回FALSE,但实际返回TRUE,说明无法处理"mindmaps"这个单词。 - cavo789
1
@cavo789 仍然正确,因为“mindmaps”是一个有效的base64字符串...您可以轻松验证自己:window.btoa("\u009a)Ý\u0099ªl") - Philzen
1
@undefined 这很有趣,因为从技术上讲,它应该抛出一个异常,因为那不是一个有效的base64字符串。一个有效的字符串应该是“SomeStringObviouslyNotBase64Encoded =”。不知道这是实现上的弱点还是按设计工作的? - Philzen
1
是的,atob 不是一个好的选项来测试一个字符串是否为 base64 编码,因为它太宽松了。它允许没有必需的 === 填充的 base64 编码字符串。Base64 编码字符串应该具有 4 的倍数长度。 - Anders Marzi Tornblad
1
@swateek对我来说有效:“base64regex.test('fuel') === true”,因为fuel是一个完全有效的编码后的base64字符串,解码为~ç¥。我怀疑您正在寻找一种函数,用于判断您是否在查看编码或纯粹的负载...但这样一个(非基于人工智能且完全确定性的)函数不存在,尽管我很乐意被证明错误。不过,这超出了本主题的范围,因为楼主明确要求一种方法来判断字符串是否包含有效的base64,而根据RFC4648,fuel就是有效的base64字符。 - Philzen
显示剩余12条评论

68

如果你想检查它是否可以被解码,你可以尝试解码并查看是否失败:

try {
    window.atob(str);
} catch(e) {
    // something failed

    // if you want to be specific and only catch the error which means
    // the base 64 was invalid, then check for 'e.code === 5'.
    // (because 'DOMException.INVALID_CHARACTER_ERR === 5')
}

3
+1,更好了。我找不到任何关于它是否必须在失败时抛出异常的说明;如果有参考链接会很方便 :) - Dave Newton
1
@Dave Newton:建议将其添加到HTML5规范中:http://lists.w3.org/Archives/Public/public-html-diffs/2011Feb/0005.html“如果输入字符串不是有效的base64数据,则抛出INVALID_CHARACTER_ERR异常。” - pimvdb
这可能是一个不错的解决方案,但它似乎在解码失败时没有抛出异常(至少在Chrome中没有)。 - Jonatan
1
有些东西可以成功解码,但它们不是base64,比如atob("krtest");请参考下面的Dan Smith的答案,了解完整的方法。 - jakraska
我不确定我喜欢使用异常来控制程序流程,但这是一个聪明的技巧。 :) - Anders Marzi Tornblad
显示剩余2条评论

51

这应该可以解决问题。

function isBase64(str) {
    if (str ==='' || str.trim() ===''){ return false; }
    try {
        return btoa(atob(str)) == str;
    } catch (err) {
        return false;
    }
}

奇怪的是,对于一些PNG图像的数据,它在我的电脑上无法工作。 - Manuel
3
不工作。不知何故许多其他字符串通过了此验证。 - zhuhang.jasper
3
为什么你认为test不是有效的Base64编码? - Anders Marzi Tornblad
如果str为null或未定义,str.trim()会终止js执行。请在第一行加入if (!str) return false;。 - Scholtz
1
一个空字符串是一个空字符串的有效base64编码。为什么你要排除它呢? - Barmar
显示剩余2条评论

39
如果“有效”意味着“只含有base64字符”,那么请检查/[A-Za-z0-9+/=]/
如果“有效”意味着一个“合法”的base64编码字符串,那么你应该检查结尾的=
如果“有效”意味着解码后是一些合理的内容,则需要相关领域知识。

3
它还可以包含加号(+)、斜杠(/)和可能的等号(=)在结尾处。 - pimvdb
1
通常取第63和64个字符作为+和/,但具体实现可能会有所不同。同时,为了使字符数为偶数,通常以一个或两个“=”字符结尾。 - user684934
@pimvdb和bdares:糟糕,是的;没有注意到。 - Dave Newton
8
请注意,某些Base64的实现方式不要求填充。仅检查'='可能不足以确定是否需要填充。http://en.wikipedia.org/wiki/Base64#Implementations_and_history - catalyst294
23
=填充并不总是存在。 - Charlie

27

我会使用正则表达式来实现。尝试使用此表达式:

/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/

解释:

^                          # Start of input
([0-9a-zA-Z+/]{4})*        # Groups of 4 valid characters decode
                           # to 24 bits of data for each group
(                          # Either ending with:
    ([0-9a-zA-Z+/]{2}==)   # two valid characters followed by ==
    |                      # , or
    ([0-9a-zA-Z+/]{3}=)    # three valid characters followed by =
)?                         # , or nothing
$                          # End of input

1
我认为这是所有答案中最好的一个。 - misantronic
SomeStringObviouslyNotBase64Encoded 测试为假,尽管它是有效的base64编码: atob("SomeStringObviouslyNotBase64Encoded") 返回 JJÚâ¾*.²\¢ÐZ±î¸w(uç。有可能改进这个正则表达式使其100%准确吗? - undefined
3
实际上,window.atob 可以接受不完全正确的字符串。你的例子恰好有35个字符,并且应该用一个等号进行填充。引用自维基百科的一句话:“当未编码输入长度不是三的倍数时,必须添加填充以使编码输出长度成为四的倍数。” - Anders Marzi Tornblad

5

这种方法尝试解码再编码,并与原始内容进行比较。在可能抛出解析错误的环境中,也可以与其他答案相结合使用。但是,有时候一个字符串在正则表达式上看起来像是有效的base64格式,但实际上并不是。

if(btoa(atob(str))==str){
  //...
}

如果str不是有效的base64,atob(str)将抛出未捕获的错误。使用try..catch语句似乎更好。 - undefined
谢谢提示!这是个好主意(比仅验证base64允许的字符要好得多)。我的nodejs实现:https://dev59.com/vmsz5IYBdhLWcg3wfn66#68286515 - mikep

5
这是我最喜欢的一个验证库中的实现方式:

这里是示例代码:

const notBase64 = /[^A-Z0-9+\/=]/i;

export default function isBase64(str) {
  assertString(str); // remove this line and make sure you pass in a string
  const len = str.length;
  if (!len || len % 4 !== 0 || notBase64.test(str)) {
    return false;
  }
  const firstPaddingChar = str.indexOf('=');
  return firstPaddingChar === -1 ||
    firstPaddingChar === len - 1 ||
    (firstPaddingChar === len - 2 && str[len - 1] === '=');
}

https://github.com/chriso/validator.js/blob/master/src/lib/isBase64.js


请参阅我关于填充的答案中的注释。 - Dave Newton
@DaveNewton 你是什么意思?那个等号不总是在那里吗?这个函数不需要 = - Lukas Liesis
关于 padding,例如,len % 4 !== 0。 - Dave Newton
根据RFC4648,只有在base16编码或数据长度已知的情况下,才可以忽略=填充。 - Philzen

3
在nodejs中的实现(不仅验证允许的字符,还验证整个base64字符串)

    const validateBase64 = function(encoded1) {
        var decoded1 = Buffer.from(encoded1, 'base64').toString('utf8');
        var encoded2 = Buffer.from(decoded1, 'binary').toString('base64');
        return encoded1 == encoded2;
    }


2

对我而言,如果一个字符串满足以下条件,则很可能是编码为base64:

  1. 长度可以被4整除
  2. 只使用 A-Z a-z 0-9 +/=
  3. 只在结尾使用 =(0-2个字符)

因此代码应该是:

function isBase64(str)
{
    return str.length % 4 == 0 && /^[A-Za-z0-9+/]+[=]{0,2}$/.test(str);
}

即使它是有效的base64,"isBase64("SomeStringObviouslyNotBase64Encoded")"返回FALSE。 - undefined
@undefined 不行,因为你需要用“=”填充它,参见RFC。需要一个填充字符,以便内容长度始终可被24位(或64进制字符的4个字符)整除。 - willnode
1
在JavaScript中,使用(我相信现在已经过时的)atobbtoa以及推荐的Buffer.from("...", "base64")据我所知不需要使用=进行填充。我看到许多项目因为各种原因而删除了填充=字符,但是这样的字符串仍然可以在JS中进行base64解码而不会抛出错误。您的答案针对问题的要点,我在此留下任何想要检查字符串是否可以解码而不是检查它是否与实际RFC定义匹配的用户。 - undefined

1
我尝试了下面的答案,但是还存在一些问题。
var base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
base64regex.test(value)

当使用此功能时,"BBBBB"应为大写字母。同时,"4444"也应为真值。
我为自己添加了一些代码,以便正确工作。
function (value) {
  var base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
  if (base64regex.test(value) && isNaN(value) && !/^[a-zA-Z]+$/.test(value)) {
  return decodeURIComponent(escape(window.atob(value)));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接