奇怪的Base64编码/解码问题

Ric*_*sky 18 html grails groovy base64 encoding

我正在使用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
Run Code Online (Sandbox Code Playgroud)

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

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

data = data.replaceAll(' ', '+')
Run Code Online (Sandbox Code Playgroud)

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

代码太大了,不能在这里发布,但我得到了base64编码,如下所示:

def inputFile = new File(inputFilename)
def rawData =  inputFile.getBytes()
def encoded = rawData.encodeBase64().toString()
Run Code Online (Sandbox Code Playgroud)

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

def encodedFile = new File(encodedFilename)
String encoded = encodedFile.getText()
byte[] rawData = encoded.decodeBase64()
Run Code Online (Sandbox Code Playgroud)

所以这一切都很好.现在假设我接受"编码"变量并将其添加到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"
}
Run Code Online (Sandbox Code Playgroud)

在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()
Run Code Online (Sandbox Code Playgroud)

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

ike*_*ami 13

无论哪种填充都params希望请求是URL编码形式(具体地说,application/x-www-form-urlencoded"+"表示空格),但您没有对其进行URL编码.我不知道你的语言提供了什么功能,但在伪代码中,queryString应该构造

concat(uri_escape("data"), "=", uri_escape(base64_encode(rawBytes)))
Run Code Online (Sandbox Code Playgroud)

这简化为

concat("data=", uri_escape(base64_encode(rawBytes)))
Run Code Online (Sandbox Code Playgroud)

" +"字符将替换为" %2B".


Pol*_*lak 7

您必须使用特殊的 base64encode,它也是 url 安全的。问题是标准 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, '-_,', '+/='));

    }
Run Code Online (Sandbox Code Playgroud)