Я использую Grails 1.3.7. У меня есть код, который использует встроенную функцию base64Encode и функцию base64Decode. Все это прекрасно работает в простых тестовых случаях, когда я кодирую некоторые двоичные данные, а затем декодирую полученную строку и записываю ее в новый файл. В этом случае файлы идентичны.
Но потом я написал веб-службу, которая взяла данные, закодированные base64, в качестве параметра в вызове POST. Хотя длина данных base64 идентична строке I, переданной в функцию, содержимое данных base64 изменяется. Я потратил DAYS на отладку и, наконец, написал тестовый контроллер, который передал данные в base64 для публикации, а также взял имя локального файла с правильными закодированными данными base64, как в:
data=AAA-base-64-data...&testFilename=/name/of/file/with/base64data
В рамках тестовой функции я сравнивал каждый байт в параметре входящих данных с соответствующим байтом в тестовом файле. Я обнаружил, что каким-то образом каждый символ "+" в параметре входных данных был заменен на "" (пробел, порядковый номер ascii 32). А? Что могло бы это сделать?
Чтобы быть уверенным, что я был прав, я добавил строку, в которой говорилось:
data = data.replaceAll(' ', '+')
и, конечно же, данные будут декодированы точно вправо. Я пробовал его с произвольно длинными двоичными файлами, и теперь он работает каждый раз. Но я не могу понять для меня, что изменит параметр данных в сообщении, чтобы преобразовать символ ord (43) в ord (32)? Я знаю, что знак плюса является одним из двух немного зависимых от платформы символов в спецификации 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()
Извините за длинный разглагольствование, но мне очень хотелось бы понять, почему мне пришлось "заменить пространство знаком плюс", чтобы это правильно декодировало. Есть ли проблема с знаком "плюс", который используется в параметре запроса?