Einfache Möglichkeit, Inhalte eines Java InputStream in einen OutputStream zu schreiben
Frage
Ich war überrascht, heute festzustellen, dass ich keine einfache Möglichkeit finden konnte, den Inhalt eines zu schreiben InputStream
zu einem OutputStream
in Java.Offensichtlich ist der Byte-Puffer-Code nicht schwer zu schreiben, aber ich vermute, dass mir einfach etwas fehlt, das mir das Leben einfacher (und den Code klarer) machen würde.
Also, angesichts einer InputStream
in
und ein OutputStream
out
, Gibt es eine einfachere Möglichkeit, Folgendes zu schreiben?
byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
out.write(buffer, 0, len);
len = in.read(buffer);
}
Lösung
Wenn Sie Java 7 verwenden, Dateien (in der Standardbibliothek) ist der beste Ansatz:
/* You can get Path from file also: file.toPath() */
Files.copy(InputStream in, Path target)
Files.copy(Path source, OutputStream out)
Bearbeiten:Natürlich ist es nur nützlich, wenn Sie einen InputStream oder OutputStream aus einer Datei erstellen.Verwenden file.toPath()
um den Pfad aus der Datei zu erhalten.
Um in eine bestehende Datei zu schreiben (z.B.einer erstellt mit File.createTempFile()
), müssen Sie die bestehen REPLACE_EXISTING
Kopieroption (sonst FileAlreadyExistsException
ist geworfen):
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING)
Andere Tipps
Wie WMR erwähnte, org.apache.commons.io.IOUtils
von Apache hat eine Methode namens copy(InputStream,OutputStream)
das genau das tut, was Sie suchen.
Also hast du:
InputStream in;
OutputStream out;
IOUtils.copy(in,out);
in.close();
out.close();
...in deinem Code.
Gibt es einen Grund, warum Sie es meiden? IOUtils
?
Java 9
Seit Java 9, InputStream
stellt eine Methode namens bereit transferTo
mit folgender Signatur:
public long transferTo(OutputStream out) throws IOException
Als die Dokumentation Zustände, transferTo
Wille:
Liest alle Bytes aus diesem Eingabestream und schreibt die Bytes in den angegebenen Ausgangsstrom in der Reihenfolge, in der sie gelesen werden.Bei der Rückkehr befindet sich dieser Eingangsstrom am Ende des Streams.Diese Methode schließt keiner Stream.
Diese Methode kann auf unbestimmte Zeit aus dem Eingabestream blockieren oder in den Ausgabestream schreiben.Das Verhalten für den Fall, in dem der Eingangs- und/oder Ausgangsstrom asynchron geschlossen oder während der Übertragung unterbrochen wird, ist stark Eingangs- und Ausgangsstromspezifikationen und daher nicht angegeben
Also um Inhalte eines Java zu schreiben InputStream
zu einem OutputStream
, Du kannst schreiben:
input.transferTo(output);
Ich denke, das wird funktionieren, aber testen Sie es unbedingt ...Kleinere „Verbesserung“, aber die Lesbarkeit könnte etwas darunter leiden.
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
Verwendung von Guaven ByteStreams.copy()
:
ByteStreams.copy(inputStream, outputStream);
Einfache Funktion
Wenn Sie dies nur zum Schreiben einer benötigen InputStream
zu einem File
Dann können Sie diese einfache Funktion verwenden:
private void copyInputStreamToFile( InputStream in, File file ) {
try {
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=in.read(buf))>0){
out.write(buf,0,len);
}
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
PipedInputStream
Und PipedOutputStream
sollte nur verwendet werden, wenn Sie mehrere Threads haben, z vom Javadoc notiert.
Beachten Sie außerdem, dass Eingabestreams und Ausgabestreams keine Thread-Unterbrechungen mit einschließen IOException
S...Daher sollten Sie erwägen, eine Unterbrechungsrichtlinie in Ihren Code zu integrieren:
byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
out.write(buffer, 0, len);
len = in.read(buffer);
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
Dies wäre eine nützliche Ergänzung, wenn Sie diese API zum Kopieren großer Datenmengen oder von Daten aus Streams verwenden möchten, die unerträglich lange hängen bleiben.
Der JDK
verwendet den gleichen Code, daher scheint es keinen „einfacheren“ Weg ohne klobige Bibliotheken von Drittanbietern zu geben (die wahrscheinlich sowieso nichts anderes machen).Das Folgende wird direkt kopiert java.nio.file.Files.java
:
// buffer size used for reading and writing
private static final int BUFFER_SIZE = 8192;
/**
* Reads all bytes from an input stream and writes them to an output stream.
*/
private static long copy(InputStream source, OutputStream sink)
throws IOException
{
long nread = 0L;
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = source.read(buf)) > 0) {
sink.write(buf, 0, n);
nread += n;
}
return nread;
}
Für diejenigen, die es verwenden Federrahmen es gibt ein nützliches StreamUtils Klasse:
StreamUtils.copy(in, out);
Dadurch werden die Streams nicht geschlossen.Wenn Sie möchten, dass die Streams nach dem Kopieren geschlossen werden, verwenden Sie FileCopyUtils Klasse stattdessen:
FileCopyUtils.copy(in, out);
Mit JDK-Methoden geht das nicht viel einfacher, aber wie Apocalsp bereits bemerkt hat, sind Sie nicht der Einzige mit dieser Idee:Du könntest benutzen IOUtils aus Jakarta Commons IO, es hat auch viele andere nützliche Dinge, die meiner Meinung nach eigentlich Teil des JDK sein sollten ...
Mit Java7 und Versuchen Sie es mit Ressourcen, kommt mit einer vereinfachten und lesbaren Version.
try(InputStream inputStream = new FileInputStream("C:\\mov.mp4");
OutputStream outputStream = new FileOutputStream("D:\\mov.mp4")){
byte[] buffer = new byte[10*1024];
for (int length; (length = inputStream.read(buffer)) != -1; ){
outputStream.write(buffer, 0, length);
}
}catch (FileNotFoundException exception){
exception.printStackTrace();
}catch (IOException ioException){
ioException.printStackTrace();
}
Verwenden Sie die Util-Klasse von Commons Net:
import org.apache.commons.net.io.Util;
...
Util.copyStream(in, out);
Hier erfahren Sie, wie ich es mit der einfachsten for-Schleife mache.
private void copy(final InputStream in, final OutputStream out)
throws IOException {
final byte[] b = new byte[8192];
for (int r; (r = in.read(b)) != -1;) {
out.write(b, 0, r);
}
}
Ein meiner Meinung nach minimalerer Ausschnitt (der auch die Längenvariable enger begrenzt):
byte[] buffer = new byte[2048];
for (int n = in.read(buffer); n >= 0; n = in.read(buffer))
out.write(buffer, 0, n);
Nebenbei bemerkt verstehe ich nicht, warum nicht mehr Leute a verwenden for
Schleife, stattdessen entscheiden Sie sich für a while
mit einem Zuweisungs- und Testausdruck, der von manchen als „schlechter“ Stil angesehen wird.
Ich denke, es ist besser, einen großen Puffer zu verwenden, da die meisten Dateien größer als 1024 Byte sind.Außerdem empfiehlt es sich, zu überprüfen, ob die Anzahl der gelesenen Bytes positiv ist.
byte[] buffer = new byte[4096];
int n;
while ((n = in.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
out.close();
PipedInputStream und PipedOutputStream können von Nutzen sein, da Sie sie miteinander verbinden können.
Ein weiterer möglicher Kandidat sind die Guava I/O-Dienstprogramme:
http://code.google.com/p/guava-libraries/wiki/IOExplained
Ich dachte, ich würde diese verwenden, da Guava in meinem Projekt bereits äußerst nützlich ist, anstatt noch eine weitere Bibliothek für eine Funktion hinzuzufügen.
ich benutze BufferedInputStream
Und BufferedOutputStream
um die Puffersemantik aus dem Code zu entfernen
try (OutputStream out = new BufferedOutputStream(...);
InputStream in = new BufferedInputStream(...))) {
int ch;
while ((ch = in.read()) != -1) {
out.write(ch);
}
}
public static boolean copyFile(InputStream inputStream, OutputStream out) {
byte buf[] = new byte[1024];
int len;
long startTime=System.currentTimeMillis();
try {
while ((len = inputStream.read(buf)) != -1) {
out.write(buf, 0, len);
}
long endTime=System.currentTimeMillis()-startTime;
Log.v("","Time taken to transfer all bytes is : "+endTime);
out.close();
inputStream.close();
} catch (IOException e) {
return false;
}
return true;
}
Versuchen Kakteen:
new LengthOf(new TeeInput(input, output)).value();
Weitere Details hier: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html
Sie können diese Methode verwenden
public static void copyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}