طريقة سهلة لكتابة محتويات جافا InputStream إلى OutputStream

StackOverflow https://stackoverflow.com/questions/43157

  •  09-06-2019
  •  | 
  •  

سؤال

وقد فوجئت اليوم أنه لا يمكنني تعقب أي طريقة بسيطة لكتابة محتويات InputStream إلى OutputStream في جافا.ومن الواضح بايت العازلة رمز ليس من الصعب أن يكتب ، ولكن أظن أنا في عداد المفقودين شيء الذي من شأنه أن يجعل حياتي أسهل (و الكود أكثر وضوحا).

لذا تعطى InputStream in و OutputStream out, هل هناك طريقة أسهل أن تكتب ما يلي ؟

byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
    out.write(buffer, 0, len);
    len = in.read(buffer);
}
هل كانت مفيدة؟

المحلول

إذا كنت تستخدم جافا 7, الملفات (في المكتبة القياسية) هو أفضل نهج:

/* You can get Path from file also: file.toPath() */
Files.copy(InputStream in, Path target)
Files.copy(Path source, OutputStream out)

تحرير:بالطبع انها مفيدة فقط عندما يمكنك إنشاء واحدة من InputStream أو OutputStream من الملف.استخدام file.toPath() للحصول على مسار الملف.

إلى إرسال إلى القائمة ملف (على سبيل المثالواحدة تم إنشاؤها مع File.createTempFile()), سوف تحتاج إلى تمرير REPLACE_EXISTING الخيار نسخة (وإلا FileAlreadyExistsException يتم طرح):

Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING)

نصائح أخرى

كما WMR المذكورة ، org.apache.commons.io.IOUtils من أباتشي لديه طريقة تسمى copy(InputStream,OutputStream) الذي يفعل بالضبط ما تبحث عنه.

لذلك يجب:

InputStream in;
OutputStream out;
IOUtils.copy(in,out);
in.close();
out.close();

في التعليمات البرمجية الخاصة بك.

هل هناك سبب أنت تتجنبين IOUtils?

جافا 9

منذ جافا 9, InputStream يوفر طريقة تسمى transferTo مع التوقيع التالي:

public long transferTo(OutputStream out) throws IOException

كما الوثائق الدول ، transferTo سوف:

يقرأ كل بايت من هذا تيار الإدخال و يكتب بايت إلى بالنظر إلى دفق إخراج في الترتيب الذي يتم قراءة.على العودة ، تيار الإدخال سوف يكون في نهاية تيار.هذا الأسلوب لا يغلق أما تيار.

هذا الأسلوب قد منع لأجل غير مسمى القراءة من تيار الإدخال ، أو الكتابة إلى دفق إخراج.السلوك عن حالة الإدخال أو الإخراج ستريم بشكل غير متزامن مغلقة ، أو موضوع توقف خلال نقل عالية المدخلات والمخرجات تيار معين ، وبالتالي غير محدد

وذلك من أجل كتابة محتويات جافا InputStream إلى OutputStream, يمكنك أن تكتب:

input.transferTo(output);

أعتقد أن هذا سوف تعمل ، ولكن تأكد من اختبار...طفيفة "تحسين", ولكن قد يكون قليلا من التكلفة في القراءة.

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
    out.write(buffer, 0, len);
}

باستخدام الجوافة ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

وظيفة بسيطة

إذا كنت بحاجة فقط هذا بالنسبة كتابة InputStream إلى File ثم يمكنك استخدام هذه وظيفة بسيطة:

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 و PipedOutputStream وينبغي أن تستخدم فقط عندما يكون لديك العديد من المواضيع ، لاحظ جافادوك.

لاحظ أيضا أن إدخال تيارات والإخراج تيارات لا التفاف أي موضوع انقطاع مع IOExceptions...لذا يجب عليك أن تنظر في إدراج انقطاع سياسة الكود الخاص بك:

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();
    }
}

هذا من شأنه أن يكون إضافة مفيدة إذا كنت تتوقع أن استخدام هذا API نسخ كميات كبيرة من البيانات أو البيانات من الجداول التي تتعثر لمدة لا يطاق منذ وقت طويل.

على JDK يستخدم نفس الكود لذلك يبدو وكأنه لا يوجد "أسهل" طريقة دون عالي الكعب طرف ثالث المكتبات (والتي ربما لا تفعل أي شيء مختلف على أي حال).التالية هي نسخ مباشرة من 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;
    }

بالنسبة لأولئك الذين يستخدمون الربيع إطار هناك المفيدة StreamUtils الدرجة:

StreamUtils.copy(in, out);

أعلاه لا يغلق تيارات.إذا كنت تريد تيارات مغلقة بعد نسخ أو استخدام FileCopyUtils فئة بدلا من ذلك:

FileCopyUtils.copy(in, out);

ليس هناك طريقة للقيام بذلك أسهل كثيرا مع JDK الطرق ، ولكن كما Apocalisp وقد لوحظ بالفعل ، أنت لست الوحيد مع هذه الفكرة:هل يمكن استخدام IOUtils من جاكرتا العموم IO, ، كما أن لديها الكثير من الأشياء الأخرى المفيدة ، أن المنظمة ينبغي أن يكون في الواقع جزء من جدك...

باستخدام Java7 ، حاول مع الموارد, يأتي مع تبسيط للقراءة الإصدار.

    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();
    }

استخدام المشاع صافي Util الدرجة:

import org.apache.commons.net.io.Util;
...
Util.copyStream(in, out);

هنا يأتي كيف أفعل مع أبسط حلقة.

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);
    }
}

أ IMHO أكثر الحد الأدنى مقتطف (وهذا أيضا أضيق النطاقات طول متغير):

byte[] buffer = new byte[2048];
for (int n = in.read(buffer); n >= 0; n = in.read(buffer))
    out.write(buffer, 0, n);

كملاحظة جانبية, أنا لا أفهم لماذا أكثر الناس لا تستخدم for حلقة ، بدلا من اختياره while مع تعيين-و-اختبار التعبير الذي يعتبر من قبل البعض "الفقراء" الاسلوب.

أعتقد أنه من الأفضل استخدام المخزن مؤقت كبير ، لأن معظم ملفات أكبر من 1024 بايت.كما انها ممارسة جيدة للتحقق من عدد من قراءة بايت أن تكون إيجابية.

byte[] buffer = new byte[4096];
int n;
while ((n = in.read(buffer)) > 0) {
    out.write(buffer, 0, n);
}
out.close();

PipedInputStream و PipedOutputStream قد يكون من استخدام بعض ، كما يمكنك الاتصال واحدة إلى أخرى.

مرشح محتمل آخر هي الجوافة I/O المرافق:

http://code.google.com/p/guava-libraries/wiki/IOExplained

فكرت في استخدام هذه منذ الجوافة هي بالفعل مفيدة جدا في المشروع بدلا من إضافة بعد آخر المكتبة للحصول على وظيفة واحدة.

يمكنني استخدام BufferedInputStream و BufferedOutputStream لإزالة التخزين المؤقت دلالات من التعليمات البرمجية

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;
}

محاولة Cactoos:

new LengthOf(new TeeInput(input, output)).value();

المزيد من التفاصيل هنا: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

يمكنك استخدام هذا الأسلوب

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){}
 }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top