Jak przekonwertować InputStream na plik wirtualny

82

Mam metodę, która oczekuje, że jedna ze zmiennych wejściowych będzie typu java.io.File, ale otrzymuję tylko InputStream. Nie mogę też zmienić podpisu metody.

Jak mogę przekonwertować InputStream na typ pliku bez faktycznego zapisywania pliku w systemie plików?

Baran
źródło
Czy to jest funkcja wbudowana? czy jest to funkcja niestandardowa?
Richard J. Ross III
2
@Richard: "Nie mogę zmienić podpisu metody."
BalusC
Dlaczego właściwie potrzebujesz Fileobiektu zamiast InputStream?
Poindexter
2
Nie mogę używać InputStream, ponieważ API, którego używam, nakazuje Plik
Ram
Wydaje mi się, że ktoś zaprojektował zepsute API, które akceptuje tylko pliki zamiast InputStreams, a teraz musisz użyć tego API
Mike76

Odpowiedzi:

122

Coś takiego powinno działać. Zauważ, że dla uproszczenia użyłem funkcji Java 7 (spróbuj blokować z zamykanym zasobem) i IOUtils z Apache commons-io. Jeśli nie możesz ich użyć, potrwa to trochę dłużej, ale ten sam pomysł.

import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class StreamUtil {

    public static final String PREFIX = "stream2file";
    public static final String SUFFIX = ".tmp";

    public static File stream2file (InputStream in) throws IOException {
        final File tempFile = File.createTempFile(PREFIX, SUFFIX);
        tempFile.deleteOnExit();
        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            IOUtils.copy(in, out);
        }
        return tempFile;
    }

}
cobbzilla
źródło
8
Czy createTempFile faktycznie nie zapisuje prawdziwego pliku, który można zobaczyć w systemie plików?
programista Androida
jesteś technicznie poprawny. używając powyższego kodu, plik tymczasowy będzie istniał do momentu zakończenia programu lub usunięcia go przez program.
cobbzilla
Dokumentacja deleteOnExit mówi, że nie powinieneś polegać na tym, aby działał (i myślę, że w rzeczywistości nawet nic nie robi): developer.android.com/reference/java/io/… . Ponadto, jeśli wiesz, jak przesyłać strumieniowo dźwięk do odtwarzania, bez żadnego pliku, napisz o tym tutaj: stackoverflow.com/q/36552381/878126
programista Androida
4
Ogólnie rzecz biorąc, wolę, aby aplikacja usuwała plik tymczasowy, gdy ma to sens. Używam go deleteOnExitjako dodatkowego sprawdzenia poczytalności, nigdy nie polegając na nim bezwarunkowo. Wiem, że w pewnych sytuacjach ( kill -9na przykład JVM) zaczepy zamykające, które wykonują usuwanie, nigdy nie zostaną wywołane.
cobbzilla
Jeśli używasz tego na Androidzie, dodaj implementation 'org.apache.directory.studio:org.apache.commons.io:2.4'do gradle.
LordParsley
30

Nie możesz. Strumień wejściowy to po prostu ogólny strumień danych i nie ma gwarancji, że faktycznie pochodzi z pliku. Gdyby ktoś utworzył InputStream z odczytu usługi internetowej lub po prostu przekonwertował String na InputStream, nie byłoby sposobu, aby połączyć to z plikiem. Więc jedyne, co możesz zrobić, to zapisać dane ze strumienia do pliku tymczasowego (np. Używając metody File.createTempFile) i przesłać ten plik do swojej metody.

Jan Thomä
źródło
8
Rzeczywiście, File#createTempFile()to naprawdę najlepszy wybór.
BalusC
7
Zobacz także File#deleteOnExit(), co powoduje, że maszyna JVM próbuje automatycznie usunąć plik po zakończeniu pracy maszyny JVM. Oczywiście najlepiej jest usunąć plik tymczasowy ręcznie i natychmiast, gdy wiesz, że plik nie jest już potrzebny i nie jest już używany. Ale w niedoskonałym świecie czasami konieczne są niedoskonałe rozwiązania.
Mike Clark