代码之家  ›  专栏  ›  技术社区  ›  Axel Richter

使用java.awt.image.BufferedImage创建BIFF8位图记录需要很多时间-有没有更好的方法?

  •  4
  • Axel Richter  · 技术社区  · 8 年前

    所以我创造了一个 HSSFSheet 使用设置背景位图 apache poi 拥有低级代码。这个 https://www.openoffice.org/sc/excelfileformat.pdf Record BITMAP, BIFF8 :

    ...

    在每一行中,所有像素都是从左到右写入的。每个像素存储为3字节数组:像素颜色的红色、绿色和蓝色分量,按此顺序排列。通过在最后一个像素后插入零字节,将每行的大小对齐为4的倍数。

    完整声明见PDF图片: enter image description here

    为了实现这一点,我的方法是 java.awt.image.BufferedImage 有类型的 BufferedImage.TYPE_3BYTE_BGR . 然后以正确的顺序(从底线到顶线)从BuffereImage的光栅中获取所有字节R G B,并在宽度(x方向)上填充到4的倍数。

    参见代码:

    import java.io.FileOutputStream;
    import java.io.FileInputStream;
    
    import org.apache.poi.hssf.usermodel.*;
    
    import org.apache.poi.hssf.record.RecordBase;
    import org.apache.poi.hssf.record.StandardRecord;
    import org.apache.poi.hssf.model.InternalSheet;
    import org.apache.poi.util.LittleEndianOutput;
    
    import java.lang.reflect.Field;
    
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    import java.awt.image.BufferedImage;
    import java.awt.Graphics2D;
    
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    
    import javax.imageio.ImageIO;
    
    public class CreateExcelHSSFSheetBackgroundBitmap {
    
     static List<Byte> getBackgroundBitmapData(String filePath) throws Exception {
    
      //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
      List<Byte> data = new ArrayList<Byte>();
    
      // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
      BufferedImage in = ImageIO.read(new FileInputStream(filePath));
      BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
      Graphics2D graphics = image.createGraphics();
      graphics.drawImage(in, null, 0, 0);
      graphics.dispose();
    
      short width = (short)image.getWidth();
      short height = (short)image.getHeight();
    
      // each pixel has 3 bytes but the width bytes must be filled up to multiple of 4
      int widthBytesMultOf4 = (int)((width * 3 + 3) / 4 * 4);
    
    // --- this part takes much time but I have not found any better possibility
    
      // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
      int bytes = 0;
      for (short y = (short)(height - 1); y >= 0; y--) {
       for (short x = 0; x < width; x++) {
        int r = image.getData().getSample(x, y, 2);
        data.add(Byte.valueOf((byte)r));
        bytes++;
        int g = image.getData().getSample(x, y, 1);
        data.add(Byte.valueOf((byte)g));
        bytes++;
        int b = image.getData().getSample(x, y, 0);
        data.add(Byte.valueOf((byte)b));
        bytes++;
       } 
       // fill up x with 0 bytes up to multiple of 4
       for (int x = width * 3; x < widthBytesMultOf4; x++) {
        data.add(Byte.valueOf((byte)0));
        bytes++;
       }
      }
    
    // ---
    
      // size  12 bytes (additional headers, see below) + picture bytes
      int size = 12 + bytes;
    
      // get size int as LITTLE_ENDIAN bytes
      ByteBuffer bSize = ByteBuffer.allocate(4);
      bSize.order(ByteOrder.LITTLE_ENDIAN);
      bSize.putInt(size);
    
      // get width short as LITTLE_ENDIAN bytes
      ByteBuffer bWidth = ByteBuffer.allocate(2);
      bWidth.order(ByteOrder.LITTLE_ENDIAN);
      bWidth.putShort(width);
    
      // get height short as LITTLE_ENDIAN bytes
      ByteBuffer bHeight = ByteBuffer.allocate(2);
      bHeight.order(ByteOrder.LITTLE_ENDIAN);
      bHeight.putShort(height);
    
      // put the record headers into the data
      Byte[] dataPart = new Byte[] { 0x09, 0x00, 0x01, 0x00, 
         bSize.array()[0], bSize.array()[1], bSize.array()[2], bSize.array()[3], // size
         //now 12 bytes follow
         0x0C, 0x00, 0x00, 0x00, 
         bWidth.array()[0], bWidth.array()[1], // width
         bHeight.array()[0], bHeight.array()[1], // height
         0x01, 0x00, 0x18, 0x00
       }; 
    
      data.addAll(0, Arrays.asList(dataPart));
    
      return data;
     }
    
     public static void main(String[] args) throws Exception {
    
      HSSFWorkbook workbook = new HSSFWorkbook();
      HSSFSheet sheet = workbook.createSheet("Sheet1");
      sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set
    
      // we need the binary records of the sheet
      // get InternalSheet
      Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
      _sheet.setAccessible(true); 
      InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet); 
    
      // get List of RecordBase
      Field _records = InternalSheet.class.getDeclaredField("_records");
      _records.setAccessible(true);
      @SuppressWarnings("unchecked") 
      List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
    
      // get bytes of the image file
      List<Byte> data = getBackgroundBitmapData("dummyText.png"); //PNG must not have transparency
    
      // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
      BitmapRecord bitmapRecord = null;
      List<ContinueRecord> continueRecords = new ArrayList<ContinueRecord>();
      int bytes = 0;
      if (data.size() > 8220) {
       bitmapRecord = new BitmapRecord(data.subList(0, 8220));
       bytes = 8220;
       while (bytes < data.size()) {
        if ((bytes + 8220) < data.size()) {
         continueRecords.add(new ContinueRecord(data.subList(bytes, bytes + 8220)));
         bytes += 8220;
        } else {
         continueRecords.add(new ContinueRecord(data.subList(bytes, data.size())));
         break;
        }
       }
      } else {
       bitmapRecord = new BitmapRecord(data);
      }
    
      // add the records after PageSettingsBlock
      int i = 0;
      for (RecordBase r : records) {
       if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
        break;
       }
       i++;
      }
      records.add(++i, bitmapRecord);
      for (ContinueRecord continueRecord : continueRecords) {
       records.add(++i, continueRecord);  
      }
    
      // debug output
      for (RecordBase r : internalsheet.getRecords()) {
       System.out.println(r);
      }
    
      // write out workbook
      workbook.write(new FileOutputStream("CreateExcelHSSFSheetBackgroundBitmap.xls"));
      workbook.close();
    
     }
    
     static class BitmapRecord extends StandardRecord {
    
      //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
      List<Byte> data = new ArrayList<Byte>();
    
      BitmapRecord(List<Byte> data) {
       this.data = data;
      }
    
      public int getDataSize() { 
       return data.size(); 
      }
    
      public short getSid() {
       return (short)0x00E9;
      }
    
      public void serialize(LittleEndianOutput out) {
       for (Byte b : data) {
        out.writeByte(b);
       }
      }
     }
    
     static class ContinueRecord extends StandardRecord {
    
      //see https://www.openoffice.org/sc/excelfileformat.pdf - CONTINUE
    
      List<Byte> data = new ArrayList<Byte>();
    
      ContinueRecord(List<Byte> data) {
       this.data = data;
      }
    
      public int getDataSize() { 
       return data.size(); 
      }
    
      public short getSid() {
       return (short)0x003C;
      }
    
      public void serialize(LittleEndianOutput out) {
       for (Byte b : data) {
        out.writeByte(b);
       }
      }
     }
    
    }
    

    代码起作用,但是

    // --- this part takes much time but I have not found any better possibility
    

    和

    // ---
    

    有人知道更好的方法吗?也许上面这种奇怪的格式并不像我想的那么奇怪,而且已经有其他的用法了?

    2 回复  |  直到 8 年前
        1
  •  2
  •   Harald K    8 年前

    这是你的代码的一个修改版本,对我来说很有效,而且速度很快。

    1. 我在用 byte[] ByteArrayOutputStream )到处都是,再也没有了 List<Byte> .
    2. 因为我们已经有了 BufferedImage 属于 TYPE_3BYTE_BGR ,我们几乎可以直接将其用作BMP输出。我们只需要a)预先准备一个有效的BMP头,b)自下而上写入,c)将每个扫描线(行)填充到32位边界,d)切换BGR->RGB顺序。
    3. Raster 将(填充的)数据行复制到输出中,因为复制较大的数据块比复制单个字节快。

    正如在评论中已经指出的,该结构是一个标准的BMP,带有 BITMAPCOREHEADER (没有文件头)。不幸的是 ImageIO BMPImageWriter 始终写入文件头并使用 BITMAPINFOHEADER 也就是40字节。您可能可以绕过这些问题,使用标准的writer,只需稍微处理一下数据(提示:文件头包含偏移量为10的像素数据的偏移量),但由于核心BMP格式的实现非常简单,所以可能也很容易做到以下几点。

    Bitmap/ContinueRecord Arrays.copyOfRange() ).

    代码:

    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.awt.image.Raster;
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.lang.reflect.Field;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    
    import org.apache.poi.hssf.model.InternalSheet;
    import org.apache.poi.hssf.record.RecordBase;
    import org.apache.poi.hssf.record.StandardRecord;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.util.LittleEndianOutput;
    
    public class CreateExcelHSSFSheetBackgroundBitmap {
    
        static byte[] getBackgroundBitmapData(String filePath) throws Exception {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
            // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
            BufferedImage in = ImageIO.read(new FileInputStream(filePath));
            BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
            Graphics2D graphics = image.createGraphics();
            graphics.drawImage(in, null, 0, 0);
            graphics.dispose();
    
            // calculate row size (c)
            int rowSize = ((24 * image.getWidth() + 31) / 32) * 4;
    
            ByteArrayOutputStream output = new ByteArrayOutputStream(image.getHeight() * rowSize * 3 + 1024);
    
            // put the record headers into the data
            ByteBuffer header = ByteBuffer.allocate(8 + 12);
            header.order(ByteOrder.LITTLE_ENDIAN);
    
            // Undocumented XLS stuff
            header.putShort((short) 0x09);
            header.putShort((short) 0x01);
            header.putInt(image.getHeight() * rowSize + 12); // Size of image stream
    
            // BITMAPCOREHEADER (a)
            header.putInt(12);
    
            header.putShort((short) image.getWidth());
            header.putShort((short) image.getHeight()); // Use -height if writing top-down
    
            header.putShort((short) 1); // planes, always 1
            header.putShort((short) 24); // bitcount
    
            output.write(header.array());
    
            // Output rows bottom-up (b)
            Raster raster = image.getRaster()
                                 .createChild(0, 0, image.getWidth(), image.getHeight(), 0, 0, new int[]{2, 1, 0}); // Reverse BGR -> RGB (d)
            byte[] row = new byte[rowSize]; // padded (c)
    
            for (int i = image.getHeight() - 1; i >= 0; i--) {
                row = (byte[]) raster.getDataElements(0, i, image.getWidth(), 1, row);
                output.write(row);
            }
    
            return output.toByteArray();
        }
    
        public static void main(String[] args) throws Exception {
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set
    
            // we need the binary records of the sheet
            // get InternalSheet
            Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
            _sheet.setAccessible(true);
            InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet);
    
            // get List of RecordBase
            Field _records = InternalSheet.class.getDeclaredField("_records");
            _records.setAccessible(true);
            @SuppressWarnings("unchecked")
            List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
    
            // get bytes of the image file
            byte[] data = getBackgroundBitmapData("dummy.png"); //PNG must not have transparency
    
            // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
            BitmapRecord bitmapRecord;
            List<ContinueRecord> continueRecords = new ArrayList<>();
            int bytes;
    
            if (data.length > 8220) {
                bitmapRecord = new BitmapRecord(Arrays.copyOfRange(data, 0, 8220));
                bytes = 8220;
                while (bytes < data.length) {
                    if ((bytes + 8220) < data.length) {
                        continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, bytes + 8220)));
                        bytes += 8220;
                    } else {
                        continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, data.length)));
                        break;
                    }
                }
            } else {
                bitmapRecord = new BitmapRecord(data);
            }
    
            // add the records after PageSettingsBlock
            int i = 0;
            for (RecordBase r : records) {
                if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
                    break;
                }
                i++;
            }
            records.add(++i, bitmapRecord);
            for (ContinueRecord continueRecord : continueRecords) {
                records.add(++i, continueRecord);
            }
    
            // debug output
            for (RecordBase r : internalsheet.getRecords()) {
                System.out.println(r);
            }
    
            // write out workbook
            workbook.write(new FileOutputStream("backgroundImage.xls"));
            workbook.close();
    
        }
    
        static class BitmapRecord extends StandardRecord {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
            byte[] data;
    
            BitmapRecord(byte[] data) {
                this.data = data;
            }
    
            public int getDataSize() {
                return data.length;
            }
    
            public short getSid() {
                return (short)0x00E9;
            }
    
            public void serialize(LittleEndianOutput out) {
                out.write(data);
            }
        }
    
        static class ContinueRecord extends StandardRecord {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - CONTINUE
    
            byte[] data;
    
            ContinueRecord(byte[] data) {
                this.data = data;
            }
    
            public int getDataSize() {
                return data.length;
            }
    
            public short getSid() {
                return (short)0x003C;
            }
    
            public void serialize(LittleEndianOutput out) {
                out.write(data);
            }
        }
    }
    
        2
  •  0
  •   Axel Richter    8 年前

    通常这只是程序员自己的愚蠢;—)。在德语中有一句谚语:“看不见森林是因为有树。”。

    Raster 的 BufferedImage 一次又一次地跳出环路而不是进入环路,会极大地提高速度:

    ...
      // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
      int bytes = 0;
      Raster raster = image.getData();
      for (short y = (short)(height - 1); y >= 0; y--) {
       for (short x = 0; x < width; x++) {
        int r = raster.getSample(x, y, 2);
        data.add(Byte.valueOf((byte)r));
        bytes++;
        int g = raster.getSample(x, y, 1);
        data.add(Byte.valueOf((byte)g));
        bytes++;
        int b = raster.getSample(x, y, 0);
        data.add(Byte.valueOf((byte)b));
        bytes++;
       } 
       // fill up x with 0 bytes up to multiple of 4
       for (int x = width * 3; x < widthBytesMultOf4; x++) {
        data.add(Byte.valueOf((byte)0));
        bytes++;
       }
      }
    ...
    

    这个 BkHim公司 记录指定纸张(1)背景的图像数据

    cf(2字节) :指定图像格式的有符号整数。必须是以下值:

    值:0x0009

    意思:位图格式。图像数据以位图格式存储,如[MSDN-BMP]所述

    值:0x000E

    意思:本机格式。图像数据以另一个应用程序的本机格式存储,不能直接处理。

    0x000E 第一个字节而不是 0x0009 然后可以直接存储本地图片字节(PNG、JPG、BMP……)。明天试试这个。


    嗯,微软的“文档”往往只是徒劳的努力。这个 https://interoperability.blob.core.windows.net/files/MS-XLS/[MS-XLS].pdf 打他。所以使用 0x000E 而不是 0x0009 不会简单地允许存储本机 imageBlob

    0x0009

    位图格式。图像数据以位图格式存储,如中所述 [MSDN-BMP]

    只有链接到什么样的位图必须在这里使用不完整的描述。没有关于头结构的开始和像素字节从底线到顶行。而且也不需要在最后一个像素后插入零字节,将每行的大小调整为4的倍数。但即使使用位图也不知道这些 0x0009 不起作用。

    当我把任何一种背景图像放进工作表中 *.xls Excel 的GUI,然后使用十六进制转储查看该文件,则始终如下所示:

    e900型 09000100 SSSSSSSS 0亿 wwwhhh 01001800 购买力平价。。。

    https://www.openoffice.org/sc/excelfileformat.pdf 比微软的“文档”更有用。

    所以下面的代码将背景图像放入 HSSFSheet 适用于我测试多种不同类型的图像文件(BMP、PNG、JPG)。

    也改变了使用 List<Byte> data 到 byte[] data . 所以呢 org.apache.poi.hssf.record.ContinueRecord

    import java.io.FileOutputStream;
    import java.io.FileInputStream;
    import java.io.ByteArrayOutputStream;
    
    import org.apache.poi.hssf.usermodel.*;
    
    import org.apache.poi.hssf.record.RecordBase;
    import org.apache.poi.hssf.record.StandardRecord;
    import org.apache.poi.hssf.record.ContinueRecord;
    import org.apache.poi.hssf.model.InternalSheet;
    
    import org.apache.poi.util.LittleEndianOutput;
    
    import java.lang.reflect.Field;
    
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    import java.awt.image.BufferedImage;
    import java.awt.image.Raster;
    import java.awt.Graphics2D;
    
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    
    import javax.imageio.ImageIO;
    
    public class CreateExcelHSSFSheetBackgroundBMP {
    
     static byte[] getBackgroundBitmapData(String filePath) throws Exception {
    
      // see https://www.openoffice.org/sc/excelfileformat.pdf - 5.6 BITMAP
      // and https://interoperability.blob.core.windows.net/files/MS-XLS/[MS-XLS].pdf - 2.4.19 BkHim
    
      // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
      BufferedImage in = ImageIO.read(new FileInputStream(filePath));
      BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
      Graphics2D graphics = image.createGraphics();
      graphics.drawImage(in, null, 0, 0);
      graphics.dispose();
    
      short width = (short)image.getWidth();
      short height = (short)image.getHeight();
    
      // each pixel has 3 bytes but the width bytes must be filled up to multiple of 4
      int widthBytesMultOf4 = (int)((width * 3 + 3) / 4 * 4);
    
      // size 12 bytes (additional headers, see below) + picture bytes
      int size = 12 + height * widthBytesMultOf4;
    
      // create the header section
      ByteBuffer headers = ByteBuffer.allocate(20);
      headers.order(ByteOrder.LITTLE_ENDIAN);
      headers.putShort((short)0x09); // 0x0009 = signed integer that specifies the image format BMP
      headers.putShort((short)0x01); // reserved (2 bytes): MUST be 0x0001
      headers.putInt(size); // signed integer that specifies the size of imageBlob in bytes
      // BMP header section:
      headers.putInt(0x0C); // length 0x0C = 12 bytes
      headers.putShort(width); // pixels width
      headers.putShort(height); // pixels heigth
      headers.putShort((short)0x01); // number of planes: always 1
      headers.putShort((short)0x18); // color depth 0x018 = 24 bit
    
      //create data ByteArrayOutputStream
      ByteArrayOutputStream data = new ByteArrayOutputStream();
    
      // write headers section
      data.write(headers.array());
    
      // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
      Raster raster = image.getData();
      for (short y = (short)(height - 1); y >= 0; y--) {
       for (short x = 0; x < width; x++) {
        int r = raster.getSample(x, y, 2);
        data.write((byte)r);
        int g = raster.getSample(x, y, 1);
        data.write((byte)g);
        int b = raster.getSample(x, y, 0);
        data.write((byte)b);
       } 
       // fill up x with 0 bytes up to multiple of 4
       for (int x = width * 3; x < widthBytesMultOf4; x++) {
        data.write((byte)0);
       }
      }
    
      return data.toByteArray();
     }
    
     public static void main(String[] args) throws Exception {
    
      HSSFWorkbook workbook = new HSSFWorkbook();
      HSSFSheet sheet = workbook.createSheet("Sheet1");
      sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set
    
      // we need the binary records of the sheet
      // get InternalSheet
      Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
      _sheet.setAccessible(true); 
      InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet); 
    
      // get List of RecordBase
      Field _records = InternalSheet.class.getDeclaredField("_records");
      _records.setAccessible(true);
      @SuppressWarnings("unchecked") 
      List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
    
      // get bytes of the image file
      byte[] data = getBackgroundBitmapData("dummyText.png"); //PNG must not have transparency
    
      // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
      BitmapRecord bitmapRecord = null;
      List<ContinueRecord> continueRecords = new ArrayList<ContinueRecord>();
      int bytes = 0;
      if (data.length > 8220) {
       bitmapRecord = new BitmapRecord(Arrays.copyOfRange(data, 0, 8220));
       bytes = 8220;
       while (bytes < data.length) {
        if ((bytes + 8220) < data.length) {
         continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, bytes + 8220)));
         bytes += 8220;
        } else {
         continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, data.length)));
         break;
        }
       }
      } else {
       bitmapRecord = new BitmapRecord(data);
      }
    
      // add the records after PageSettingsBlock
      int i = 0;
      for (RecordBase r : records) {
       if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
        break;
       }
       i++;
      }
      records.add(++i, bitmapRecord);
      for (ContinueRecord continueRecord : continueRecords) {
       records.add(++i, continueRecord);  
      }
    
      // debug output
      for (RecordBase r : internalsheet.getRecords()) {
       System.out.println(r.getClass());
      }
    
      // write out workbook
      FileOutputStream out = new FileOutputStream("CreateExcelHSSFSheetBackgroundBMP.xls");
      workbook.write(out);
      workbook.close();
      out.close();
    
     }
    
     static class BitmapRecord extends StandardRecord {
    
      // see https://www.openoffice.org/sc/excelfileformat.pdf - 5.6 BITMAP
      // and https://interoperability.blob.core.windows.net/files/MS-XLS/[MS-XLS].pdf - 2.4.19 BkHim
    
      byte[] data;
    
      BitmapRecord(byte[] data) {
       this.data = data;
      }
    
      public int getDataSize() { 
       return data.length; 
      }
    
      public short getSid() {
       return (short)0x00E9;
      }
    
      public void serialize(LittleEndianOutput out) {
       out.write(data);
      }
     }
    
    }
    
    推荐文章