J'ai essayé de résoudre un problème de mémoire dans un programme Java où nous le chargement d'un fichier en mémoire, Base64, puis l'utiliser comme un paramètre de forme dans une demande de poste. Ceci est la cause oome en raison de la taille du fichier extrêmement grand.
Je travaille sur une solution où je suis en mesure de diffuser le fichier via un encodeur base64, dans le corps de la requête d'une demande Http Post. L' un des modèles communs , je l' ai remarqué dans toutes les bibliothèques d'encodage populaires (goyave, java.util.Base64, android.util.Base64 et org.apache.batik.util) est que si les supports bibliothèque l' encodage avec Streams, l'encodage est toujours fait par un OutputStream et décodage est toujours fait par un InputStream.
J'ai du mal à trouver / déterminer le raisonnement derrière ces décisions. Étant donné que beaucoup de ces bibliothèques populaires et bien écrit aligner avec cette conception api, je suppose qu'il ya une raison pour cela. Il ne semble pas très difficile d'adapter un de ces décodeurs pour devenir un InputStream ou accepter un InputStream, mais je me demande s'il y a une raison valable architecturale ces codeurs sont conçus de cette façon.
Pourquoi les bibliothèques communes font l'encodage base64 par un décodage OuputStream et base64 par un InputStream?
Exemples pour sauvegarder mes réclamations:
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
Eh bien, oui, vous pouvez inverser, mais ce qui rend le plus de sens. Base64 est utilisé pour faire des données binaires - générées ou exploitées par l'application - compatible avec un texte basé sur l' environnement extérieur. Ainsi , la base de 64 données codées est toujours nécessaire à l'extérieur et les données binaires décodé est nécessaire à l'intérieur.
Une application ne fonctionne pas généralement toutes opérations sur la base 64 codé de données elle - même; il est simplement nécessaire de communiquer des données binaires avec une autre application lorsqu'une interface texte est nécessaire ou prévu .
Si vous voulez exporter vos données binaires vers l'extérieur, vous naturellement utiliser un flux de sortie. Si que les besoins de données à coder en base 64, vous assurez-vous d'envoyer les données à un flux de sortie qui encode base 64.
Si vous souhaitez importer vos données binaires de l'extérieur que vous pouvez ensuite utiliser un flux d'entrée. Si ces données sont codées dans la base 64, alors vous devez d'abord de le décoder, vous assurez-vous de le décoder avant de le traiter comme un flux binaire.
Permet de créer un peu d'une image. Disons que vous avez une application qui fonctionne dans un environnement orienté textuel, mais fonctionne sur des données binaires. La partie importante est la direction des flèches du contexte de l'application sur la gauche.
Ensuite, vous obtenez pour l'entrée (lire les appels):
{APPLICATION} <- (binary data decoding) <- (base64 decoding) <- (file input stream) <- [BASE 64 ENCODED FILE]
Pour cela, vous utilisez naturellement les flux d'entrée.
Donc, le regard let à la sortie (appels d'écriture):
{APPLICATION} -> (binary data encoding) -> (base64 encoding) -> (file output stream) -> [BASE 64 ENCODED FILE]
Pour cela, vous utilisez naturellement des flux de sortie.
Ces flux peuvent être connectés les uns aux autres par les enchaînant ensemble , par exemple en utilisant un courant en tant que parent de l'autre flux.
Voici un exemple en Java. Notez que la création du codeur binaire / décodeur dans la classe de données lui-même est un peu laid; vous généralement utiliser une autre classe pour que - je l'espère suffit à des fins de démonstration.
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);
}
}
Noter en particulier l'enchaînement des cours d' eau et bien sûr l'absence de tampons que ce soit . Je l' ai utilisé la base d'URL de sécurité 64 (au cas où vous souhaitez utiliser HTTP GET à la place).
Dans votre cas, bien sûr, vous pouvez générer une requête HTTP POST en utilisant une URL et directement encode au récupéré OutputStream
flux en l' enveloppant. De cette façon , aucune base 64 besoins de données codées pour être (largement) en mémoire tampon. Voir des exemples sur la façon de se rendre à l' OutputStream
ici .
Rappelez-vous, si vous avez besoin de tampon, vous le faites mal.
Comme il est mentionné dans les commentaires, HTTP POST n'a pas besoin d'encodage base 64, mais que ce soit, vous savez maintenant comment vous pouvez coder base 64 directement à une connexion HTTP.
java.util.Base64
Note spécifique: Bien que la base 64 est un texte, le flux base64 génère / octets consume; il suppose simplement le codage ASCII (cela peut être amusant pour le texte UTF-16). Personnellement , je pense que c'est une terrible décision de conception; ils auraient enveloppé un Reader
et au Writer
lieu, même si cela ralentit légèrement encodant.
Pour leur défense, les différentes normes de base 64 et RFC également obtenir ce mal.