Странная проблема кодирования / декодирования Base64

Я использую Grails 1.3.7. У меня есть код, который использует встроенную функцию base64Encode и функцию base64Decode. Все это отлично работает в простых тестовых случаях, когда я кодирую некоторые двоичные данные, а затем декодирую полученную строку и записываю ее в новый файл. В этом случае файлы идентичны.

Но затем я написал веб-сервис, который использовал данные в кодировке base64 в качестве параметра при вызове POST. Хотя длина данных base64 идентична строке, которую я передал в функцию, содержимое данных base64 изменяется. Я потратил ДНИ на отладку и, наконец, написал тестовый контроллер, который передавал данные в base64 для публикации, а также взял имя локального файла с правильными данными в кодировке base64, например:

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

В тестовой функции я сравнил каждый байт в параметр входящих данных с соответствующим байтом в тестовом файле. Я обнаружил, что каким-то образом каждый символ «+» в параметре входных данных был заменен на «» (пробел, порядковый номер ascii 32). А? Что могло это сделать?

Чтобы быть уверенным, что я прав, я добавил строку, которая гласила:

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

и, конечно же, данные декодированы точно. Я пробовал использовать его с произвольно длинными двоичными файлами, и теперь он работает каждый раз. Но я не могу понять, хоть убей, что изменило бы параметр данных в сообщении, чтобы преобразовать символ ord (43) в ord (32)? Я знаю, что знак «плюс» - это один из 2-х, отчасти зависимых от платформы символов в спецификации base64, но, учитывая, что я выполняю кодирование и декодирование на одной и той же машине, я очень озадачен, чем это вызвано. Конечно, у меня есть «исправление», поскольку я могу заставить его работать, но я нервничаю по поводу «исправлений», которых я не понимаю.

Код слишком велик, чтобы публиковать здесь, но я получаю кодировку 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"
}

на стороне веб-службы, в контроллере я получаю такой параметр:

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()

Извините за длинную тираду, но мне бы очень хотелось понять почему мне пришлось сделать "заменить пробел знаком плюс", чтобы это декодировалось правильно. Есть ли проблема с использованием знака плюса в параметре запроса?

18
задан Rich Sadowsky 11 April 2011 в 23:29
поделиться