Я пытался решить проблему памяти в программе Java, где мы загружаем весь файл в память, base64 кодирования его, а затем использовать его в качестве параметра формы в запросе поста. Это вызывает OOME из-за чрезвычайно большого размера файла.
Я работаю над решением , где я в состоянии потока файла через base64 кодер, в теле запроса текста запроса HTTP Post. Одним из наиболее распространенных моделей я заметил во всех популярных библиотеках кодирования (гуава, java.util.Base64, android.util.Base64 и org.apache.batik.util) является то , что если библиотека поддерживает кодирование с Streams, кодирование всегда делается через OutputStream и декодирование всегда выполняется через InputStream.
У меня возникли проблемы с поиском / определение обоснования этих решений. Учитывая, что многие из этих популярных и хорошо написанных библиотек выравнивать с этим проектом апите, я полагаю, что есть причина для этого. Это, кажется, не очень сложно адаптировать один из этих декодеров, чтобы стать InputStream или принять в InputStream, но мне интересно, если есть действительная архитектурная причина кодеры разработаны таким образом.
Почему общие библиотеки делают Base64 кодирование через OuputStream и Base64 декодирования через InputStream?
Примеры для резервного копирования моих требований:
java.util.Base64
- Base64.Decoder.wrap(InputStream stream)
- Base64.Encoder.wrap(OutputStream stream)
android.util.Base64
- Base64InputStream // An InputStream that does Base64 decoding on the data read through it.
- Base64OutputStream // An OutputStream that does Base64 encoding
google.common.io.BaseEncoding
- decodingStream(Reader reader)
- encodingStream(Writer writer)
org.apache.batik.util
- Base64DecodeStream implements InputStream
- Base64EncodeStream implements OutputStream
Ну да, вы можете повернуть его вспять, но это имеет наибольший смысл. Base64 используется для двоичных данных - генерируется или оперировал приложение - совместим с внешней средой на основе текста. Таким образом, база 64 кодированных данные всегда требуется на внешней стороне и декодированные двоичные данные требуются на внутренней стороне .
Приложение как правило , не выполняет каких - либо операций на 64 базовых кодируются самих данных; это просто необходимо общаться двоичные данные с другим приложением , когда текстовый интерфейс требуется или ожидается .
Если вы хотите экспортировать двоичные данные наружу, естественно, вы бы использовать выходной поток. Если что данные должны быть закодированы в базе 64, вы убедитесь, что вы посылаете данные в выходной поток, который кодирует к основанию 64.
Если вы хотите импортировать двоичные данные с внешней стороны, то вы бы использовать входной поток. Если данные кодируются в базе 64, то в первую очередь необходимо декодировать его, так что вы убедитесь, что вы расшифровать его перед обработкой его в виде двоичного потока.
Позволяет создать немного картины. Скажем, у вас есть приложение, которое работает в текстовом ориентированной среде, но работает на двоичных данных. Важной частью является направление стрелок из контекста приложения слева.
После этого вы получаете для ввода (чтения вызовов):
{APPLICATION} <- (binary data decoding) <- (base64 decoding) <- (file input stream) <- [BASE 64 ENCODED FILE]
для этого вы, естественно, использовать входные потоки.
Так выглядят ДАВАЙТЕ на выходных (звонки записи):
{APPLICATION} -> (binary data encoding) -> (base64 encoding) -> (file output stream) -> [BASE 64 ENCODED FILE]
для этого вы, естественно, использовать выходные потоки.
Эти поток могут быть соединены друг с другом посредством сцепления их вместе , то есть , используя один поток в качестве родителя другого потока.
Вот пример в Java. Обратите внимание, что создание двоичного кодера / декодера в самом классе данных является немного некрасиво; как правило, вы будете использовать другой класс для этого - я надеюсь, что это достаточно для демонстрационных целей.
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;
public class BinaryHandlingApplication {
/**
* A data class that encodes to binary output, e.g. to interact with an application in another language.
*
* Binary format: [32 bit int element string size][UTF-8 element string][32 bit element count]
* The integers are signed, big endian values.
* The UTF-8 string should not contain a BOM.
* Note that this class doesn't know anything about files or base 64 encoding.
*/
public static class DataClass {
private String element;
private int elementCount;
public DataClass(String element) {
this.element = element;
this.elementCount = 1;
}
public String getElement() {
return element;
}
public void setElementCount(int count) {
this.elementCount = count;
}
public int getElementCount() {
return elementCount;
}
public String toString() {
return String.format("%s count is %d", element, elementCount);
}
public void save(OutputStream out) throws IOException {
DataOutputStream dataOutputStream = new DataOutputStream(out);
// so here we have a chain of:
// a dataoutputstream on a base 64 encoding stream on a fileoutputstream
byte[] utf8EncodedString = element.getBytes(UTF_8);
dataOutputStream.writeInt(utf8EncodedString.length);
dataOutputStream.write(utf8EncodedString);
dataOutputStream.writeInt(elementCount);
}
public void load(InputStream in) throws IOException {
DataInputStream dataInputStream = new DataInputStream(in);
// so here we have a chain of:
// a datainputstream on a base 64 decoding stream on a fileinputstream
int utf8EncodedStringSize = dataInputStream.readInt();
byte[] utf8EncodedString = new byte[utf8EncodedStringSize];
dataInputStream.readFully(utf8EncodedString);
this.element = new String(utf8EncodedString, UTF_8);
this.elementCount = dataInputStream.readInt();
}
}
/**
* Create the a base 64 output stream to a file; the file is the text oriented
* environment.
*/
private static OutputStream createBase64OutputStreamToFile(String filename) throws FileNotFoundException {
FileOutputStream textOutputStream = new FileOutputStream(filename);
return Base64.getUrlEncoder().wrap(textOutputStream);
}
/**
* Create the a base 64 input stream from a file; the file is the text oriented
* environment.
*/
private static InputStream createBase64InputStreamFromFile(String filename) throws FileNotFoundException {
FileInputStream textInputStream = new FileInputStream(filename);
return Base64.getUrlDecoder().wrap(textInputStream);
}
public static void main(String[] args) throws IOException {
// this text file acts as the text oriented environment for which we need to encode
String filename = "apples.txt";
// create the initial class
DataClass instance = new DataClass("them apples");
System.out.println(instance);
// perform some operation on the data
int newElementCount = instance.getElementCount() + 2;
instance.setElementCount(newElementCount);
// write it away
try (OutputStream out = createBase64OutputStreamToFile(filename)) {
instance.save(out);
}
// read it into another instance, who cares
DataClass changedInstance = new DataClass("Uh yeah, forgot no-parameter constructor");
try (InputStream in = createBase64InputStreamFromFile(filename)) {
changedInstance.load(in);
}
System.out.println(changedInstance);
}
}
Особенно отметит цепочку из потоков и, конечно , отсутствия каких - либо буферов бы то ни было . Я использовал URL безопасной базы 64 (в случае , если вы хотите использовать HTTP GET вместо этого).
В вашем случае, конечно же , вы можете создать запрос HTTP POST с использованием URL и непосредственно закодировать в извлеченном OutputStream
потоке, окружив его. Таким образом , нет базы 64 закодированных данных потребности, чтобы быть (широко) в буфере. Смотрите примеры о том , как добраться до OutputStream
сюда .
Помните, что если вам нужно в буфер, вы делаете это неправильно.
Как уже упоминалось в комментариях, HTTP POST не требуется база 64 кодирования, но то, что, теперь вы знаете, как вы можете кодировать основание 64 непосредственно к соединению HTTP.
java.util.Base64
удельная примечание: Хотя базовая 64 представляет собой текст, поток генерирует base64 / потребляет байт; он просто предполагает кодировку ASCII (это может быть интересно для UTF-16 текста). Лично я считаю , что это ужасно дизайнерское решение; они должны были завернутые Reader
и Writer
вместо того, чтобы , даже если это замедляет немного кодирующие.
Для их защиты, различные базы 64 стандартов и RFC также получить это неправильно.