แปลง InputStream เป็นไบต์อาร์เรย์ใน Java


คำตอบ:


1135

คุณสามารถใช้ Apache Commons IOเพื่อจัดการงานนี้และงานที่คล้ายกัน

IOUtilsประเภทมีวิธีการคงอ่านและส่งกลับInputStreambyte[]

InputStream is;
byte[] bytes = IOUtils.toByteArray(is);

ภายในนี้จะสร้างและสำเนาไบต์เพื่อการส่งออกโทรแล้วByteArrayOutputStream toByteArray()มันจัดการไฟล์ขนาดใหญ่โดยการคัดลอกไบต์ในบล็อกของ 4KiB


188
สำหรับความต้องการในการเขียนโค้ด 4 บรรทัดคุณคิดว่าการนำเข้าการพึ่งพาจากบุคคลที่สามนั้นคุ้มค่าหรือไม่
oxbow_lakes

217
หากมีห้องสมุดที่จัดการกับความต้องการและจัดการกับการประมวลผลสำหรับไฟล์ขนาดใหญ่และผ่านการทดสอบอย่างดีแน่นอนคำถามคือทำไมฉันต้องเขียนเอง โถเป็นเพียง 107KB และหากคุณต้องการวิธีหนึ่งจากนั้นคุณมีแนวโน้มที่จะใช้วิธีอื่นเช่นกัน
Rich Seller

242
@oxbow_lakes: พิจารณาจากจำนวนเงินที่ส่ายของที่ไม่ถูกต้องการใช้งานคุณลักษณะนี้ผมเคยเห็นในชีวิตนักพัฒนาของฉันฉันรู้สึกว่าใช่มันมากคุ้มค่ามากพึ่งพาภายนอกที่จะได้รับมันขวา
Joachim Sauer

17
ทำไมไม่ลองไปดูที่ Apache คอมมอนส์อย่างเช่นFastArrayListแผนที่อ้างอิงอ่อน ๆ และกลับมาบอกฉันว่าห้องสมุดนี้ "ผ่านการทดสอบอย่างดี" แล้ว มันเป็นกองขยะ
oxbow_lakes

87
นอกเหนือไปจาก Apache Commons-io ตรวจสอบByteStreamsระดับจากGoogle ฝรั่ง InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
michaelok

446

คุณจะต้องอ่านแต่ละไบต์จากคุณและเขียนไปยังInputStreamByteArrayOutputStream

จากนั้นคุณสามารถดึงข้อมูลอาร์เรย์ไบต์พื้นฐานได้ด้วยการโทรtoByteArray():

InputStream is = ...
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[16384];

while ((nRead = is.read(data, 0, data.length)) != -1) {
  buffer.write(data, 0, nRead);
}

return buffer.toByteArray();

16
ขนาดของไบต์ที่สร้างขึ้นใหม่จะเป็นเท่าใด ทำไมถึงเป็น 16384 ฉันจะกำหนดขนาดที่แน่นอนได้อย่างไร ขอบคุณมาก.
Ondrej Bozek

6
16384 เป็นทางเลือกที่ค่อนข้างยุติธรรมแม้ว่าฉันมักจะชอบพลังของ 2 เพื่อเพิ่มโอกาสของอาเรย์ที่สอดคล้องกับขอบเขตของคำ คำตอบของ pihentagy แสดงวิธีที่คุณสามารถหลีกเลี่ยงการใช้บัฟเฟอร์กลางได้ แต่ควรจัดสรรอาร์เรย์ที่มีขนาดที่ถูกต้อง ถ้าคุณไม่ได้จัดการกับไฟล์ขนาดใหญ่ฉันชอบโค้ดข้างต้นซึ่งดูดีกว่าและสามารถใช้สำหรับ InputStreams ที่ไม่ทราบจำนวนไบต์ที่อ่านล่วงหน้า
Adamski

@Adamski ไม่ได้สร้างอาร์เรย์ของไบต์ที่ใหญ่กว่าที่คุณคาดหวังว่าข้อมูลจะอยู่ในสตรีมเสียความทรงจำใช่มั้ย
Paul Brewczynski

@bluesm: ใช่ถูกต้อง อย่างไรก็ตามในตัวอย่างของฉันอาร์เรย์ไบต์มีขนาดเพียง 16Kb และเล็กมากตามมาตรฐานของวันนี้ แน่นอนว่าหน่วยความจำนี้จะถูกปลดปล่อยอีกครั้งในภายหลัง
Adamski

5
@Adamski ฮาร์ดแวร์โครงสร้างพื้นฐานจำนวนมากเว็บเซิร์ฟเวอร์และส่วนประกอบของเลเยอร์ OS กำลังใช้บัฟเฟอร์ 4K เพื่อย้ายข้อมูลดังนั้นนี่คือเหตุผลสำหรับหมายเลขที่แน่นอน แต่ประเด็นหลักคือคุณจะได้รับประสิทธิภาพที่เพิ่มขึ้นเล็กน้อยจากการใช้ 4K ซึ่งโดยทั่วไปถือว่าเป็นการสิ้นเปลืองความจำ ฉันสมมติว่าสิ่งนี้ยังคงเป็นจริงเพราะฉันรู้มานานนับสิบปีแล้ว!

312

ในที่สุดหลังจากยี่สิบปีที่ผ่านมามีวิธีง่ายๆที่ไม่ต้องใช้ห้องสมุดของบุคคลที่สามด้วยJava 9 :

InputStream is;

byte[] array = is.readAllBytes();

โปรดทราบวิธีการอำนวยความสะดวกreadNBytes(byte[] b, int off, int len)และtransferTo(OutputStream)การตอบสนองความต้องการที่เกิดขึ้นประจำ


132

ใช้ vanilla Java DataInputStreamและreadFullyวิธีการ (มีอยู่ตั้งแต่อย่างน้อย Java 1.4):

...
byte[] bytes = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(bytes);
...

มีรสชาติอื่น ๆ ของวิธีนี้ แต่ฉันใช้มันตลอดเวลาสำหรับกรณีการใช้งานนี้


45
+1 สำหรับการใช้ไลบรารีมาตรฐานแทนการอ้างอิงของบุคคลที่สาม น่าเสียดายที่มันไม่ได้ผลสำหรับฉันเพราะฉันไม่ทราบความยาวของกระแสข้อมูลล่วงหน้า
Andrew Spencer

2
imgFile คืออะไร มันไม่สามารถเป็น InputStream ซึ่งควรจะเป็นอินพุตของวิธีนี้
Janus Troelsen

4
@janus มันคือ "ไฟล์" วิธีนี้ใช้ได้เฉพาะเมื่อคุณทราบความยาวของไฟล์หรือจำนวนไบต์ที่อ่าน
dermoritz

5
สิ่งที่น่าสนใจ แต่คุณต้องทราบความยาวที่แน่นอนของสตรีม (ส่วนหนึ่งของ) ที่จะอ่าน ยิ่งกว่านั้นคลาสDataInputStreamนี้ยังใช้เป็นหลักในการอ่านประเภทหลัก (ยาว, กางเกง, ตัวอักษร ... ) จากสตรีมเพื่อให้เราสามารถเห็นการใช้งานนี้ว่าเป็นการใช้ในทางที่ผิดของชั้นเรียน
Olivier Faucheux

17
หากคุณทราบความยาวของข้อมูลที่จะอ่านจากกระแสข้อมูลแล้วจะไม่ดีไปกว่าInputStream.readนี้
รถกระบะโลแกน


46

เช่นเคยกรอบ Spring (spring-core ตั้งแต่ 3.2.2) มีบางอย่างสำหรับคุณ:StreamUtils.copyToByteArray()


เช่นเดียวกับคนอื่น ๆ ส่วนใหญ่ฉันต้องการหลีกเลี่ยงการใช้ห้องสมุดบุคคลที่สามสำหรับบางสิ่งที่เรียบง่าย แต่ Java 9 ไม่ใช่ตัวเลือกในตอนนี้ ... โชคดีที่ฉันใช้ Spring อยู่แล้ว
scottysseus

42
public static byte[] getBytesFromInputStream(InputStream is) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    byte[] buffer = new byte[0xFFFF];
    for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { 
        os.write(buffer, 0, len);
    }
    return os.toByteArray();
}

2
มันเป็นตัวอย่างและเป็นเช่นนี้ความกะทัดรัดเป็นคำสั่งของวัน การคืนค่า null ที่นี่จะเป็นตัวเลือกที่เหมาะสมในบางกรณี (แม้ว่าในสภาพแวดล้อมการใช้งานจริงคุณจะมีการจัดการข้อยกเว้นและเอกสารประกอบที่เหมาะสม)

11
ฉันเข้าใจความกะทัดรัดในตัวอย่าง แต่ทำไมไม่เพียงทำให้ตัวอย่างโยน IOException แทนที่จะกลืนลงไปและคืนค่าไร้ความหมาย?
pendor

4
ฉันใช้เสรีภาพในการเปลี่ยนจาก 'return null' เป็น 'throw IOException'
kritzikratzi

3
ไม่จำเป็นต้องลองใช้กับทรัพยากรที่นี่เพราะ ByteArrayOutputStream # close () ไม่ได้ทำอะไรเลย (ByteArrayOutputStream # flush () ไม่จำเป็นและไม่ทำอะไรเกินไป)
Luke Hutchison

25

วิธีแก้ปัญหาอย่างปลอดภัย (ด้วยความสามารถของcloseสตรีมอย่างถูกต้อง):

  • Java 9+ เวอร์ชัน:

    final byte[] bytes;
    try (inputStream) {
        bytes = inputStream.readAllBytes();
    }
  • Java 8 เวอร์ชัน:

    public static byte[] readAllBytes(InputStream inputStream) throws IOException {
        final int bufLen = 4 * 0x400; // 4KB
        byte[] buf = new byte[bufLen];
        int readLen;
        IOException exception = null;
    
        try {
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
                    outputStream.write(buf, 0, readLen);
    
                return outputStream.toByteArray();
            }
        } catch (IOException e) {
            exception = e;
            throw e;
        } finally {
            if (exception == null) inputStream.close();
            else try {
                inputStream.close();
            } catch (IOException e) {
                exception.addSuppressed(e);
            }
        }
    }
  • รุ่นKotlin (เมื่อไม่สามารถเข้าถึง Java 9+):

    @Throws(IOException::class)
    fun InputStream.readAllBytes(): ByteArray {
        val bufLen = 4 * 0x400 // 4KB
        val buf = ByteArray(bufLen)
        var readLen: Int = 0
    
        ByteArrayOutputStream().use { o ->
            this.use { i ->
                while (i.read(buf, 0, bufLen).also { readLen = it } != -1)
                    o.write(buf, 0, readLen)
            }
    
            return o.toByteArray()
        }
    }

    เพื่อหลีกเลี่ยงการซ้อนกันuseดูที่นี่


ไม่ได้หมายความว่าในบางครั้งคุณจะมีหน่วยความจำที่ใช้เพิ่มขึ้นเป็นสองเท่าเพราะคุณมีทั้งบัฟเฟอร์และอาร์เรย์ไบต์? ไม่มีวิธีส่งไบต์โดยตรงไปยังอาร์เรย์ไบต์เอาต์พุตหรือไม่
นักพัฒนา Android

@androiddeveloper; ฉันขอโทษ. ฉันไม่รู้คำตอบ! แต่ฉันไม่คิดอย่างนั้น ฉันคิดว่าวิธีนี้ (ใช้บัฟเฟอร์) เป็นวิธีที่ดีที่สุด
Mir-Ismaili

ฉันได้ตรวจสอบแล้ว แต่มันดูเหมือนว่ามันเป็นทางออกเดียวที่คุณสามารถเลือกได้เมื่อคุณไม่ทราบขนาด ถ้าคุณรู้ขนาดแล้วคุณสามารถสร้างอาร์เรย์ด้วยขนาดที่กำหนดและเติมได้โดยตรง ดังนั้นคุณใช้ฟังก์ชั่นที่จะรับพารามิเตอร์ขนาดไบต์และถ้ามันถูกต้องใช้มันเพื่อสร้างและเติมอาร์เรย์ไบต์โดยตรงโดยไม่ต้องสร้างวัตถุขนาดใหญ่อื่น ๆ
นักพัฒนา android

@androiddeveloper; ขอบคุณสำหรับข้อมูลของคุณ. ฉันไม่รู้จักพวกเขา
Mir-Ismaili

19

คุณต้องการภาพเป็นbyte[]หรือไม่? สิ่งที่คุณคาดหวังในสิ่งที่byte[]- เนื้อหาที่สมบูรณ์ของไฟล์ภาพที่เข้ารหัสในรูปแบบใดไฟล์ภาพที่อยู่ในหรือค่าพิกเซล RGB?

คำตอบอื่น ๆ byte[]ที่นี่แสดงให้คุณเห็นวิธีการอ่านไฟล์ลงได้ คุณbyte[]จะมีเนื้อหาที่แน่นอนของไฟล์และคุณจะต้องถอดรหัสให้ทำอะไรกับข้อมูลภาพ

มาตรฐาน API ของ Java สำหรับการอ่าน (และเขียน) ภาพเป็น API ImageIO javax.imageioซึ่งคุณจะพบในแพคเกจ คุณสามารถอ่านรูปภาพจากไฟล์ที่มีโค้ดเพียงบรรทัดเดียว:

BufferedImage image = ImageIO.read(new File("image.jpg"));

นี้จะช่วยให้คุณไม่ได้BufferedImage byte[]ที่จะได้รับข้อมูลภาพคุณสามารถเรียกบนgetRaster() BufferedImageสิ่งนี้จะให้Rasterวัตถุซึ่งมีวิธีการเข้าถึงข้อมูลพิกเซล (มีหลายวิธีgetPixel()/ getPixels())

ค้นหาเอกสาร API สำหรับjavax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Rasterฯลฯ

ImageIO รองรับรูปแบบภาพจำนวนมากโดยค่าเริ่มต้น: JPEG, PNG, BMP, WBMP และ GIF เป็นไปได้ที่จะเพิ่มการรองรับรูปแบบเพิ่มเติม (คุณต้องมีปลั๊กอินที่ใช้อินเทอร์เฟซผู้ให้บริการ ImageIO)

ดูเพิ่มเติมบทช่วยสอนต่อไปนี้: การทำงานกับรูปภาพ


16

ในกรณีที่มีคนยังคงมองหาวิธีการแก้ปัญหาโดยไม่ต้องพึ่งพาและหากคุณมีไฟล์

1) DataInputStream

 byte[] data = new byte[(int) file.length()];
 DataInputStream dis = new DataInputStream(new FileInputStream(file));
 dis.readFully(data);
 dis.close();

2) ByteArrayOutputStream

 InputStream is = new FileInputStream(file);
 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 int nRead;
 byte[] data = new byte[(int) file.length()];
 while ((nRead = is.read(data, 0, data.length)) != -1) {
     buffer.write(data, 0, nRead);
 }

3) RandomAccessFile

 RandomAccessFile raf = new RandomAccessFile(file, "r");
 byte[] data = new byte[(int) raf.length()];
 raf.readFully(data);

บอกว่าจะเกิดอะไรขึ้นถ้าอาร์เรย์ไบต์มีขนาดใหญ่เกินไปซึ่งอาจทำให้ OOM เป็นกองได้ มีวิธีแก้ปัญหาคล้ายกันที่จะใช้ JNI เพื่อจัดเก็บไบต์และหลังจากนั้นเราจะสามารถใช้ inputStream จากข้อมูลที่เก็บไว้ที่นั่น (แคชชั่วคราว)
นักพัฒนา android

14

หากคุณไม่ต้องการใช้ห้องสมุด Apache Commons-io ข้อมูลโค้ดนี้นำมาจากคลาส sun.misc.IOUtils มันเร็วเกือบสองเท่าของการติดตั้งทั่วไปโดยใช้ ByteBuffers:

public static byte[] readFully(InputStream is, int length, boolean readAll)
        throws IOException {
    byte[] output = {};
    if (length == -1) length = Integer.MAX_VALUE;
    int pos = 0;
    while (pos < length) {
        int bytesToRead;
        if (pos >= output.length) { // Only expand when there's no room
            bytesToRead = Math.min(length - pos, output.length + 1024);
            if (output.length < pos + bytesToRead) {
                output = Arrays.copyOf(output, pos + bytesToRead);
            }
        } else {
            bytesToRead = output.length - pos;
        }
        int cc = is.read(output, pos, bytesToRead);
        if (cc < 0) {
            if (readAll && length != Integer.MAX_VALUE) {
                throw new EOFException("Detect premature EOF");
            } else {
                if (output.length != pos) {
                    output = Arrays.copyOf(output, pos);
                }
                break;
            }
        }
        pos += cc;
    }
    return output;
}

นี่เป็นวิธีแก้ปัญหาแปลก ๆความยาวคือขอบเขตบนของความยาวของอาร์เรย์ หากคุณทราบความยาวทั้งหมดที่คุณต้องการคือ: byte [] output = new byte [length]; is.read (output); (แต่ดูคำตอบของฉัน)
ลุคฮัทชิสัน

@ luke-hutchison อย่างที่ฉันบอกนี่เป็นวิธีแก้ปัญหาของ sun.misc.IOUtils ในกรณีทั่วไปคุณไม่ทราบขนาดของ InputStream ล่วงหน้าดังนั้นหาก (length == -1) length = Integer.MAX_VALUE มีผลบังคับใช้ วิธีนี้ใช้ได้ผลแม้ว่าความยาวที่กำหนดจะมากกว่าความยาวของ InputStream
Kristian Kraljic

@ ลุคฮัทชิสันถ้าคุณรู้ว่าคุณสามารถจัดการกับความยาวได้กี่บรรทัด หากคุณดูแต่ละคำตอบทุกคนบ่นว่าไม่มีความยาว ในที่สุดคำตอบที่เป็นมาตรฐานสามารถใช้กับ Java 7 Android และไม่ต้องใช้ไลบรารีภายนอก
Csaba ถึง

11
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
    int r = in.read(buffer);
    if (r == -1) break;
    out.write(buffer, 0, r);
}

byte[] ret = out.toByteArray();

8

@Adamski: คุณสามารถหลีกเลี่ยงบัฟเฟอร์ทั้งหมด

รหัสที่คัดลอกมาจากhttp://www.exampledepot.com/egs/java.io/File2ByteArray.html (ใช่มันเป็น verbose มาก แต่ต้องการหน่วยความจำครึ่งหนึ่งเท่ากับโซลูชันอื่น)

// Returns the contents of the file in a byte array.
public static byte[] getBytesFromFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);

    // Get the size of the file
    long length = file.length();

    // You cannot create an array using a long type.
    // It needs to be an int type.
    // Before converting to an int type, check
    // to ensure that file is not larger than Integer.MAX_VALUE.
    if (length > Integer.MAX_VALUE) {
        // File is too large
    }

    // Create the byte array to hold the data
    byte[] bytes = new byte[(int)length];

    // Read in the bytes
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length
           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
        offset += numRead;
    }

    // Ensure all the bytes have been read in
    if (offset < bytes.length) {
        throw new IOException("Could not completely read file "+file.getName());
    }

    // Close the input stream and return bytes
    is.close();
    return bytes;
}

5
ขึ้นอยู่กับการรู้ขนาดล่วงหน้า
stolsvik

2
แน่นอน แต่พวกเขาควรรู้ขนาด: "ฉันต้องการอ่านภาพ"
pihentagy

1
หากคุณรู้ขนาดแล้ว java จะให้รหัสแก่คุณ ดูคำตอบหรือ google ของฉันสำหรับ "DataInputStream" และมันเป็นวิธี readFully
dermoritz

คุณควรเพิ่มis.close()ถ้าoffset < bytes.lengthหรือInputStreamจะไม่ถูกปิดหากมีการโยนข้อยกเว้นนั้น
Jared Rummler

3
ถ้าอย่างนั้นดีกว่า, คุณควรใช้การทดลองกับทรัพยากร
pihentagy

8
Input Stream is ...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int next = in.read();
while (next > -1) {
    bos.write(next);
    next = in.read();
}
bos.flush();
byte[] result = bos.toByteArray();
bos.close();

อย่างไรก็ตามโดยปกติแล้วระบบปฏิบัติการจะบัฟเฟอร์เพียงพอสำหรับสิ่งนี้ไม่ต้องกังวลมากสำหรับไฟล์ขนาดเล็ก มันไม่เหมือนหัวของฮาร์ดดิสก์ที่จะอ่านแต่ละไบต์แยกกัน (ฮาร์ดดิสก์เป็นแผ่นกระจกหมุนที่มีข้อมูลรหัสแม่เหล็กอยู่เล็กน้อยเหมือนไอคอนแปลก ๆ ที่เราใช้สำหรับบันทึกข้อมูล: P)
Maarten Bodewes

6
@Maarten Bodewes: อุปกรณ์ส่วนใหญ่มีการถ่ายโอนแบบบล็อกดังนั้นไม่ใช่ทุกครั้งที่อ่าน () จะทำให้การเข้าถึงอุปกรณ์จริง แต่การใช้ระบบปฏิบัติการแบบเรียกต่อไบต์นั้นเพียงพอที่จะฆ่าประสิทธิภาพ ในขณะที่การตัดInputStreamในBufferedInputStreamก่อนโค้ดที่จะลด OS โทรและลดข้อเสียประสิทธิภาพการทำงานอย่างมีนัยสำคัญรหัสที่จะยังคงทำคู่มือการทำงานที่ไม่จำเป็นการคัดลอกจากบัฟเฟอร์หนึ่งไปยังอีก
Holger


2

ฉันรู้ว่ามันสายเกินไป แต่ที่นี่ฉันคิดว่าเป็นโซลูชันที่สะอาดกว่าและอ่านง่ายกว่า ...

/**
 * method converts {@link InputStream} Object into byte[] array.
 * 
 * @param stream the {@link InputStream} Object.
 * @return the byte[] array representation of received {@link InputStream} Object.
 * @throws IOException if an error occurs.
 */
public static byte[] streamToByteArray(InputStream stream) throws IOException {

    byte[] buffer = new byte[1024];
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    int line = 0;
    // read bytes from stream, and store them in buffer
    while ((line = stream.read(buffer)) != -1) {
        // Writes bytes from byte array (buffer) into output stream.
        os.write(buffer, 0, line);
    }
    stream.close();
    os.flush();
    os.close();
    return os.toByteArray();
}

4
คุณควรใช้การลองกับทรัพยากร
Victor Stafusa

การจัดระเบียบของคุณในตอนท้ายจำเป็นต้องทำในที่สุดบล็อกในกรณีที่มีข้อผิดพลาดมิฉะนั้นอาจทำให้หน่วยความจำรั่ว
MGDavies

2

Java 8 way (ขอบคุณBufferedReaderและAdam Bien )

private static byte[] readFully(InputStream input) throws IOException {
    try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
        return buffer.lines().collect(Collectors.joining("\n")).getBytes(<charset_can_be_specified>);
    }
}

โปรดทราบว่าวิธีแก้ปัญหานี้จะทำการปัดคืนค่าขนส่ง ('\ r') และอาจไม่เหมาะสม


4
Stringที่มีไว้สำหรับ OP byte[]จะขอ
FrozenFire

ไม่ใช่แค่\rนั้นอาจเป็นปัญหา เมธอดนี้แปลงไบต์เป็นอักขระและย้อนกลับอีกครั้ง (ใช้ชุดอักขระเริ่มต้นสำหรับ InputStreamReader) ไบต์ใด ๆ ที่ไม่ถูกต้องในการเข้ารหัสอักขระเริ่มต้น (เช่น -1 สำหรับ UTF-8 บน Linux) จะเกิดความเสียหายแม้อาจเปลี่ยนจำนวนไบต์
seanf

ดูเหมือนว่านี่เป็นคำตอบที่ดี แต่เป็นข้อความ ผู้ซื้อระวัง
Wheezil

1

ฉันพยายามแก้ไขคำตอบ @ numan ด้วยการแก้ไขสำหรับการเขียนข้อมูลขยะ แต่การแก้ไขถูกปฏิเสธ ในขณะที่โค้ดสั้น ๆ นี้ไม่มีอะไรที่ยอดเยี่ยม แต่ฉันไม่เห็นคำตอบที่ดีกว่านี้ นี่คือสิ่งที่สมเหตุสมผลที่สุดสำหรับฉัน:

ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; // you can configure the buffer size
int length;

while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length); //copy streams
in.close(); // call this in a finally block

byte[] result = out.toByteArray();

ไม่จำเป็นต้องปิด btw ByteArrayOutputStream ลอง / สร้างโครงสร้างที่ละเว้นเพื่อความสามารถในการอ่าน


1

ดูInputStream.available()เอกสารประกอบ:

เป็นสิ่งสำคัญอย่างยิ่งที่ต้องตระหนักว่าคุณต้องไม่ใช้วิธีนี้ในการกำหนดขนาดของคอนเทนเนอร์และสมมติว่าคุณสามารถอ่านสตรีมทั้งหมดได้โดยไม่จำเป็นต้องปรับขนาดคอนเทนเนอร์ ผู้เรียกดังกล่าวควรเขียนทุกสิ่งที่พวกเขาอ่านไปยัง ByteArrayOutputStream และแปลงให้เป็นอาร์เรย์ไบต์ หรือหากคุณกำลังอ่านจากไฟล์ File.length จะส่งคืนความยาวปัจจุบันของไฟล์ (แม้ว่าการสมมติว่าความยาวของไฟล์ไม่สามารถเปลี่ยนแปลงได้อาจไม่ถูกต้อง แต่การอ่านไฟล์นั้นจะมีชีวิตชีวา)


1

ห่อไว้ใน DataInputStream หากไม่อยู่ในตารางด้วยเหตุผลบางอย่างเพียงใช้การอ่านเพื่อตอกหมุดมันจนกว่ามันจะให้ -1 หรือบล็อกทั้งหมดที่คุณขอ

public int readFully(InputStream in, byte[] data) throws IOException {
    int offset = 0;
    int bytesRead;
    boolean read = false;
    while ((bytesRead = in.read(data, offset, data.length - offset)) != -1) {
        read = true;
        offset += bytesRead;
        if (offset >= data.length) {
            break;
        }
    }
    return (read) ? offset : -1;
}

1

เราเห็นความล่าช้าในการทำธุรกรรม AWS บางรายการในขณะที่แปลงวัตถุ S3 เป็น ByteArray

หมายเหตุ: S3 Object เป็นเอกสาร PDF (ขนาดสูงสุดคือ 3 mb)

เรากำลังใช้ตัวเลือก # 1 (org.apache.commons.io.IOUtils) เพื่อแปลงวัตถุ S3 เป็น ByteArray เราได้สังเกตเห็น S3 ให้วิธี inbuild IOUtils ในการแปลงวัตถุ S3 เป็น ByteArray เราขอให้คุณยืนยันว่าวิธีที่ดีที่สุดในการแปลงวัตถุ S3 เป็น ByteArray คืออะไรเพื่อหลีกเลี่ยงความล่าช้า

ตัวเลือกที่ 1:

import org.apache.commons.io.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

ตัวเลือก # 2:

import com.amazonaws.util.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

นอกจากนี้ยังแจ้งให้เราทราบหากเรามีวิธีอื่นที่ดีกว่าในการแปลงวัตถุ s3 เป็น bytearray


0

อีกกรณีหนึ่งเพื่อรับอาร์เรย์ไบต์ที่ถูกต้องผ่านสตรีมหลังจากส่งคำขอไปยังเซิร์ฟเวอร์และรอการตอบกลับ

/**
         * Begin setup TCP connection to PC app
         * to open integrate connection between mobile app and pc app (or mobile app)
         */
        mSocket = new Socket(IP, port);
       // mSocket.setSoTimeout(30000);

        DataOutputStream mDos = new DataOutputStream(mSocket.getOutputStream());

        String str = "MobileRequest#" + params[0] + "#<EOF>";

        mDos.write(str.getBytes());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        /* Since data are accepted as byte, all of them will be collected in the
        following byte array which initialised with accepted data length. */
        DataInputStream mDis = new DataInputStream(mSocket.getInputStream());
        byte[] data = new byte[mDis.available()];

        // Collecting data into byte array
        for (int i = 0; i < data.length; i++)
            data[i] = mDis.readByte();

        // Converting collected data in byte array into String.
        String RESPONSE = new String(data);

0

คุณกำลังทำสำเนาเพิ่มเติมหากคุณใช้ ByteArrayOutputStream หากคุณทราบความยาวของสตรีมก่อนที่จะเริ่มอ่าน (เช่น InputStream เป็น FileInputStream จริง ๆ และคุณสามารถเรียกไฟล์ file.length () บนไฟล์หรือ InputStream เป็นรายการ zipfile InputStream และคุณสามารถเรียก zipEntry ได้ length ()) จากนั้นควรเขียนลงในอาร์เรย์ byte [] โดยตรงดีกว่า - ใช้หน่วยความจำครึ่งหนึ่งและประหยัดเวลา

// Read the file contents into a byte[] array
byte[] buf = new byte[inputStreamLength];
int bytesRead = Math.max(0, inputStream.read(buf));

// If needed: for safety, truncate the array if the file may somehow get
// truncated during the read operation
byte[] contents = bytesRead == inputStreamLength ? buf
                  : Arrays.copyOf(buf, bytesRead);

หมายเหตุบรรทัดสุดท้ายด้านบนเกี่ยวข้องกับไฟล์ที่ถูกตัดทอนในขณะที่สตรีมกำลังอ่านหากคุณจำเป็นต้องจัดการกับความเป็นไปได้นั้น แต่ถ้าไฟล์ยาวขึ้นในขณะที่สตรีมกำลังอ่านเนื้อหาในอาร์เรย์ [] จะไม่ยาวขึ้น รวมถึงเนื้อหาของแฟ้มใหม่อาร์เรย์ก็จะถูกตัดทอนความยาวเก่าinputStreamLength


0

ฉันใช้สิ่งนี้

public static byte[] toByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try {
            byte[] b = new byte[4096];
            int n = 0;
            while ((n = is.read(b)) != -1) {
                output.write(b, 0, n);
            }
            return output.toByteArray();
        } finally {
            output.close();
        }
    }

2
เพิ่มคำอธิบายพร้อมคำตอบสำหรับวิธีที่คำตอบนี้ช่วย OP ในการแก้ไขปัญหาปัจจุบัน
Kяσѕρєя K

0

นี่คือเวอร์ชันคัดลอกของฉัน:

@SuppressWarnings("empty-statement")
public static byte[] inputStreamToByte(InputStream is) throws IOException {
    if (is == null) {
        return null;
    }
    // Define a size if you have an idea of it.
    ByteArrayOutputStream r = new ByteArrayOutputStream(2048);
    byte[] read = new byte[512]; // Your buffer size.
    for (int i; -1 != (i = is.read(read)); r.write(read, 0, i));
    is.close();
    return r.toByteArray();
}

2
ในขณะที่ข้อมูลโค้ดนี้อาจแก้ไขคำถามรวมถึงคำอธิบายช่วยปรับปรุงคุณภาพของโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
Ferrybig

0

Java 7 และใหม่กว่า:

import sun.misc.IOUtils;
...
InputStream in = ...;
byte[] buf = IOUtils.readFully(in, -1, false);

20
sun.misc.IOUtilsไม่ใช่“ Java 7” เป็นคลาสเฉพาะที่เป็นกรรมสิทธิ์ของการนำไปใช้งานซึ่งอาจไม่ปรากฏในการนำไปใช้งาน JRE อื่นและสามารถหายไปได้โดยไม่มีการเตือนใด ๆ ในการเผยแพร่ครั้งต่อไป
Holger


0

นี่คือรุ่นที่ปรับให้เหมาะสมซึ่งพยายามหลีกเลี่ยงการคัดลอกไบต์ข้อมูลให้มากที่สุด:

private static byte[] loadStream (InputStream stream) throws IOException {
   int available = stream.available();
   int expectedSize = available > 0 ? available : -1;
   return loadStream(stream, expectedSize);
}

private static byte[] loadStream (InputStream stream, int expectedSize) throws IOException {
   int basicBufferSize = 0x4000;
   int initialBufferSize = (expectedSize >= 0) ? expectedSize : basicBufferSize;
   byte[] buf = new byte[initialBufferSize];
   int pos = 0;
   while (true) {
      if (pos == buf.length) {
         int readAhead = -1;
         if (pos == expectedSize) {
            readAhead = stream.read();       // test whether EOF is at expectedSize
            if (readAhead == -1) {
               return buf;
            }
         }
         int newBufferSize = Math.max(2 * buf.length, basicBufferSize);
         buf = Arrays.copyOf(buf, newBufferSize);
         if (readAhead != -1) {
            buf[pos++] = (byte)readAhead;
         }
      }
      int len = stream.read(buf, pos, buf.length - pos);
      if (len < 0) {
         return Arrays.copyOf(buf, pos);
      }
      pos += len;
   }
}

0

โซลูชันใน Kotlin (จะทำงานใน Java ด้วยเช่นกัน) ซึ่งรวมถึงทั้งสองกรณีเมื่อคุณทราบขนาดหรือไม่:

    fun InputStream.readBytesWithSize(size: Long): ByteArray? {
        return when {
            size < 0L -> this.readBytes()
            size == 0L -> ByteArray(0)
            size > Int.MAX_VALUE -> null
            else -> {
                val sizeInt = size.toInt()
                val result = ByteArray(sizeInt)
                readBytesIntoByteArray(result, sizeInt)
                result
            }
        }
    }

    fun InputStream.readBytesIntoByteArray(byteArray: ByteArray,bytesToRead:Int=byteArray.size) {
        var offset = 0
        while (true) {
            val read = this.read(byteArray, offset, bytesToRead - offset)
            if (read == -1)
                break
            offset += read
            if (offset >= bytesToRead)
                break
        }
    }

ถ้าคุณรู้ขนาดมันจะช่วยให้คุณประหยัดการเพิ่มหน่วยความจำที่ใช้เป็นสองเท่าเมื่อเทียบกับโซลูชั่นอื่น ๆ (ในช่วงเวลาสั้น ๆ แต่ก็ยังมีประโยชน์) นั่นเป็นเพราะคุณต้องอ่านสตรีมทั้งหมดจนจบจากนั้นแปลงเป็นอาร์เรย์ไบต์ (คล้ายกับ ArrayList ที่คุณแปลงเป็นเพียงอาร์เรย์)

ตัวอย่างเช่นถ้าคุณใช้ Android คุณมี Uri ให้จัดการคุณสามารถลองใช้ขนาดนี้ได้:

    fun getStreamLengthFromUri(context: Context, uri: Uri): Long {
        context.contentResolver.query(uri, arrayOf(MediaStore.MediaColumns.SIZE), null, null, null)?.use {
            if (!it.moveToNext())
                return@use
            val fileSize = it.getLong(it.getColumnIndex(MediaStore.MediaColumns.SIZE))
            if (fileSize > 0)
                return fileSize
        }
        //if you wish, you can also get the file-path from the uri here, and then try to get its size, using this: https://stackoverflow.com/a/61835665/878126
        FileUtilEx.getFilePathFromUri(context, uri, false)?.use {
            val file = it.file
            val fileSize = file.length()
            if (fileSize > 0)
                return fileSize
        }
        context.contentResolver.openInputStream(uri)?.use { inputStream ->
            if (inputStream is FileInputStream)
                return inputStream.channel.size()
            else {
                var bytesCount = 0L
                while (true) {
                    val available = inputStream.available()
                    if (available == 0)
                        break
                    val skip = inputStream.skip(available.toLong())
                    if (skip < 0)
                        break
                    bytesCount += skip
                }
                if (bytesCount > 0L)
                    return bytesCount
            }
        }
        return -1L
    }

-1
/*InputStream class_InputStream = null;
I am reading class from DB 
class_InputStream = rs.getBinaryStream(1);
Your Input stream could be from any source
*/
int thisLine;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((thisLine = class_InputStream.read()) != -1) {
    bos.write(thisLine);
}
bos.flush();
byte [] yourBytes = bos.toByteArray();

/*Don't forget in the finally block to close ByteArrayOutputStream & InputStream
 In my case the IS is from resultset so just closing the rs will do it*/

if (bos != null){
    bos.close();
}

การปิดและล้างข้อมูล bos เป็นการสิ้นเปลืองการคลิกบนคีย์บอร์ด การปิดอินพุตสตรีมมีแนวโน้มที่จะช่วยได้มากขึ้น การอ่านทีละหนึ่งไบต์จะไม่มีประสิทธิภาพ ดูคำตอบของนูแมน
akostadinov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.