奇怪的Base64编码/解码问题

时间:2011-04-11 23:29:31

标签: html grails encoding groovy base64

我正在使用Grails 1.3.7。我有一些代码使用内置的base64Encode函数和base64Decode函数。在我编码一些二进制数据然后解码生成的字符串并将其写入新文件的简单测试用例中,一切正常。在这种情况下,文件是相同的。

但后来我编写了一个Web服务,它将base64编码数据作为POST调用中的参数。虽然base64数据的长度与传递给函数的字符串相同,但是base64数据的内容正在被修改。我花了DAYS调试这个,最后编写了一个测试控制器,它将base64中的数据传递给post,并且还使用正确的base64编码数据获取了本地文件的名称,如:

data=AAA-base-64-data...&testFilename=/name/of/file/with/base64data

在测试函数中,我将传入数据参数中的每个字节与测试文件中的相应字节进行了比较。我发现输入数据参数中的每个“+”字符都被替换为“”(空格,序数ascii 32)。咦?有什么可以做到的?

为了确保我是正确的,我添加了一行说:

data = data.replaceAll(' ', '+')

确定数据解码完全正确。我尝试使用任意长的二进制文件,它现在每次都可以工作。但我无法弄清楚在我的生活中将修改ord(43)字符转换为ord(32)的帖子中的数据参数是什么?我知道加号是base64规范中2个有点平台依赖的字符之一,但考虑到我现在在同一台机器上进行编码和解码,我感到非常困惑是什么导致了这个问题。当然,我有一个“修复”,因为我可以使它工作,但我对我不理解的“修复”感到紧张。

代码太大了,无法在此处发布,但我得到了base64编码,如下所示:

def inputFile = new File(inputFilename)
def rawData =  inputFile.getBytes()
def encoded = rawData.encodeBase64().toString()

然后我将编码后的字符串写入新文件中,以便稍后用它进行测试。如果我重新加载该文件,那么我得到相同的rawData:

def encodedFile = new File(encodedFilename)
String encoded = encodedFile.getText()
byte[] rawData = encoded.decodeBase64()

所以这一切都很好。现在假设我接受“编码”变量并将其添加到POST函数的参数中,如下所示:

String queryString = "data=$encoded"
String url = "http://localhost:8080/some_web_service"

def results = urlPost(url, queryString)

def urlPost(String urlString, String queryString) {
    def url = new URL(urlString)
    def connection = url.openConnection()
    connection.setRequestMethod("POST")
    connection.doOutput = true

    def writer = new OutputStreamWriter(connection.outputStream)
    writer.write(queryString)
    writer.flush()
    writer.close()
    connection.connect()

    return (connection.responseCode == 200) ? connection.content.text : "error                         $connection.responseCode, $connection.responseMessage"
}

在Web服务端,在控制器中我得到如下参数:

String data = params?.data
println "incoming data parameter has length of ${data.size()}" //confirm right size

//unless I run the following line, the data does not decode to the same source
data = data.replaceAll(' ', '+')

//as long as I replace spaces with plus, this decodes correctly, why?
byte[] bytedata = data.decodeBase64()

很抱歉长期咆哮,但我真的很想知道为什么我必须做“用加号替换空间”才能正确解码。在请求参数中使用加号是否有问题?

4 个答案:

答案 0 :(得分:13)

无论填充是什么params都希望请求是一个URL编码的表单(具体来说,application/x-www-form-urlencoded,其中“+”表示空格),但您没有对其进行URL编码。我不知道您的语言提供了哪些函数,但在伪代码中,queryString应该从

构造
concat(uri_escape("data"), "=", uri_escape(base64_encode(rawBytes)))

简化为

concat("data=", uri_escape(base64_encode(rawBytes)))

+”字符将替换为“%2B”。

答案 1 :(得分:3)

因为它是POST的参数,所以您必须对数据进行URL编码。

请参阅http://en.wikipedia.org/wiki/Percent-encoding

答案 2 :(得分:3)

您必须使用特殊的base64encode,它也是url-safe。问题是标准base64encode包含+/=字符,这些字符将被百分比编码版本替换。

http://en.wikipedia.org/wiki/Base64#URL_applications

我在php中使用以下代码:

    /**
     * Custom base64 encoding. Replace unsafe url chars
     *
     * @param string $val
     * @return string
     */
    static function base64_url_encode($val) {

        return strtr(base64_encode($val), '+/=', '-_,');

    }

    /**
     * Custom base64 decode. Replace custom url safe values with normal
     * base64 characters before decoding.
     *
     * @param string $val
     * @return string
     */
    static function base64_url_decode($val) {

        return base64_decode(strtr($val, '-_,', '+/='));

    }

答案 3 :(得分:1)

来自维基百科链接的paraquote

  

默认使用的编码基于   一般的早期版本   URI百分比编码规则,带有   修改的数量,如   换行标准化和替换   带有“+”而不是“%20”的空格

另一个隐藏的陷阱每天像我这样的网络开发人员对

知之甚少