解决poi读取excel2007出现内存溢出问题代码参考

更新时间:2023-07-20 20:26:29 阅读: 评论:0

AbstractExcel2007Writer.java
package l.outofmemory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.urmodel.DateUtil;
import org.apache.poi.xssf.urmodel.XSSFSheet;
import org.apache.poi.xssf.urmodel.XSSFWorkbook;
/**
* 抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的l,使用这种方法
* 写入.xlsx文件,不需要太大的内存
*
*/
public abstract class AbstractExcel2007Writer {
   
    private SpreadsheetWriter sw;
    /**
    * 写入电子表格的主要流程
    * @param fileName
    * @throws Exception
    */
    public void process(String fileName) throws Exception{
        // 建立工作簿和电子表格对象
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("sheet1");
        // 持有电子表格数据的xml文件名 例如 /xl/l
        String sheetRef = PackagePart().getPartName().getName();
        // 保存模板
        FileOutputStream os = new FileOutputStream("template.xlsx");
        wb.write(os);
        os.clo();
       
        // 生成xml文件
        File tmp = File.createTempFile("sheet", ".xml");
        Writer fw = new FileWriter(tmp);
        sw = new SpreadsheetWriter(fw);
        generate();
        fw.clo();
       
        // 使用产生的数据替换模板
        File templateFile = new File("template.xlsx");
        FileOutputStream out = new FileOutputStream(fileName);
        substitute(templateFile, tmp, sheetRef.substring(1), out);
        out.clo();
        //删除文件之前调用一下垃圾回收器,否则无法删除模板文件
        System.gc();
        // 删除临时模板文件
        if (templateFile.isFile()&&ists()){
            templateFile.delete();
        }
    }
    /**
    * 类使用者应该使用此方法进行写操作
    * @throws Exception
    */
    public abstract void generate() throws Exception;
    public void beginSheet() throws IOException {
        sw.beginSheet();
    }
    public void inrtRow(int rowNum) throws IOException {
        sw.inrtRow(rowNum);
    }
    public void createCell(int columnIndex, String value) throws IOException {
        sw.createCell(columnIndex, value, -1);
    }
    public void createCell(int columnIndex, double value) throws IOException {
        sw.createCell(columnIndex, value, -1);
    }
    public void endRow() throws IOException {
        sw.endRow();
    }
    public void endSheet() throws IOException {
        sw.endSheet();
    }
    /**
    *
    * @param zipfile the template file
    * @param tmpfile the XML file with the sheet data
    * @param entry the name of the sheet entry to substitute, e.g. xl/l
    * @param out the stream to write the result to
    */
    private static void substitute(File zipfile, File tmpfile, String entry,
            OutputStream out) throws IOException {
        ZipFile zip = new ZipFile(zipfile);
        ZipOutputStream zos = new ZipOutputStream(out);
        @SuppressWarnings("unchecked")
        Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) ies();
        while (en.hasMoreElements()) {
            ZipEntry ze = en.nextElement();
            if (!ze.getName().equals(entry)) {
                zos.putNextEntry(new Name()));
                InputStream is = InputStream(ze);
                copyStream(is, zos);
                is.clo();
            }
        }
        zos.putNextEntry(new ZipEntry(entry));
        InputStream is = new FileInputStream(tmpfile);
        copyStream(is, zos);
        is.clo();
        zos.clo();
    }
    private static void copyStream(InputStream in, OutputStream out)
            throws IOException {
        byte[] chunk = new byte[1024];
        int count;
        while ((count = in.read(chunk)) >= 0) {
            out.write(chunk, 0, count);
        }
    }
    /**
    * 在写入器中写入电子表格
    *
    */
    public static class SpreadsheetWriter {
        private final Writer _out;
        private int _rownum防已黄芪汤;
        private static String LINE_SEPARATOR = System.getProperty("line.parator");
        public SpreadsheetWriter(Writer out) {
            _out = out;
        }
        public void beginSheet() throws IOException {
            _out.write("<?xml version=\"1.0\" encoding=\"GB2312\"?>"
                            + "<worksheet xmlns=\"schemas.openxmlformats/spreadsheetml/2006/main\">");
            _out.write("<sheetData>"+LINE_SEPARATOR);
        }
        public void endSheet() throws IOException {
            _out.write("</sheetData>");
            _out.write("</worksheet>");
        }
        /**
        * 插入新行
        *
        * @param rownum 0开始
洗衣液十大品牌
        */
        public void inrtRow(int rownum) throws IOException {
            _out.write("<row r=\"" + (rownum + 1) + "\">"+LINE_SEPARATOR);
            this._rownum = rownum;
        }
        /**
        * 插入行结束标志
        */
        public void endRow() throws IOException {
            _out.write("</row>"+LINE_SEPARATOR);
        }
        /**
        * 插入新列
        * @param columnIndex
        * @param value
        * @param styleIndex
        * @throws IOException
        */
        public void createCell(int columnIndex, String value, int styleIndex)
                throws IOException {
            String ref = new CellReference(_rownum, columnIndex)
                    .formatAsString();
            _out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");
            if (styleIndex != -1)
                _out.write(" s=\"" + styleIndex + 名书推荐"\"");
            _out.write(">");
            _out.write("<is><t>"+XMLEncoder.encode(value)+"</t></is>");
            _out.write("</c>");
        }
        public void createCell(int columnIndex, String value)
                throws IOException {
            createCell(columnIndex, value, -1);
        }
        public void createCell(int columnIndex, double value, int梦见和丈夫离婚 styleIndex)
                throws IOException {
            String ref = new CellReference(_rownum, columnIndex)
                    .formatAsString();
            _out.write("<c r=\"" + ref + "\" t=\"n\"");
            if (styleIndex != -1)
                _out.write(" s=\"" + styleIndex + "\"");
            _out.write(">");
            _out.write("<v>" + value + "</v>");
            _out.write("</c>");
        }
        public void createCell(int columnIndex, double value)
                throws IOException {
            createCell(columnIndex, value, -1);
        }
        public void createCell(int columnIndex, Calendar value, int styleIndex)
                throws IOException {
            createCell(columnIndex, DateUtil.getExcelDate(value, fal),
                    styleIndex);
        }
    }
}
Excel2003Reader.java
package l.outofmemory;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.eventurmodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventurmodel.HSSFEventFactory;
import org.apache.poi.hssf.eventurmodel.HSSFListener;
import org.apache.poi.hssf.eventurmodel.HSSFRequest;
import org.apache.poi.hssf.eventurmodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventurmodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.eventurmodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventurmodel.dummyrecord.MissingCellDummyRecord;
import org.apache.del.HSSFFormulaParr;
import org.apache.d.BOFRecord;
import org.apache.d.BlankRecord;
import org.apache.d.BoolErrRecord;
import org.apache.d.BoundSheetRecord;
import org.apache.d.FormulaRecord;
import org.apache.d.LabelRecord;
import org.apache.d.LabelSSTRecord;
import org.apache.d.NumberRecord;
import org.apache.d.Record;
import org.apache.d.SSTRecord;
import org.apache.d.StringRecord;
import org.apache.poi.hssf.urmodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* 抽象Excel2003读取器,通过实现HSSFListener监听器,采用事件驱动模式解析excel2003
* 中的内容,遇到特定事件才会触发,大大减少了内存的使用。
*
*/
public  class Excel2003Reader implements HSSFListener{
    private int minColumns = -1;
    private POIFSFileSystem fs;
    private int lastRowNumber;
    private int lastColumnNumber;
    /** Should we output the formula, or the value it has? */
    private boolean outputFormulaValues = true;
    /** For parsing Formulas */
    private SheetRecordCollectingListener workbookBuildingListener;
    //excel2003工作薄
    private HSSFWorkbook stubWorkbook;
    // Records we pick up as we process
    private SSTRecord sstRecord;
    private FormatTrackingHSSFListener formatListener;
    //表索引
    private int sheetIndex = -1;
    private BoundSheetRecord[] orderedBSRs;
    @SuppressWarnings("unchecked")
    private ArrayList boundSheetRecords = new ArrayList();
    // For handling formulas with string results
    private int nextRow;
    private int nextColumn;
    private boolean outputNextStringRecord;
    //当前行
    private int curRow = 0;
    //存储行记录的容器
    private List<String> rowlist = new ArrayList<String>();;
    @SuppressWarnings( "unud")
    private String sheetName;
   
    private IRowReader rowReader;
   
    public void tRowReader(IRowReader rowReader){
        this.rowReader = rowReader;
    }
   
    /**
    * 遍历excel下所有的sheet
    * @throws IOException
    */
    public void process(String fileName) throws IOException {
        this.fs = new POIFSFileSystem(new FileInputStream(fileName));
        MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(
                this);
        formatListener = new FormatTrackingHSSFListener(listener);
        HSSFEventFactory factory = new HSSFEventFactory();
        HSSFRequest request = new HSSFRequest();
        if (outputFormulaValues) {
            request.addListenerForAllRecords(formatListener);
        } el {
            workbookBuildingListener = new SheetRecordCollectingListener(
                    formatListener);
            request.addListenerForAllRecords(workbookBuildingListener);
        }
        factory.processWorkbookEvents(request, fs);
    }
   
读书卡片
    /**
    * HSSFListener 监听方法,处理 Record
    */
    @SuppressWarnings("unchecked")
    public void processRecord(Record record) {
        int thisRow = -1;
        int thisColumn = -1;
        String thisStr = null;
        String value = null;
        switch (Sid()) {
            ca BoundSheetRecord.sid:
                boundSheetRecords.add(record);
                break;
            ca BOFRecord.sid:
                BOFRecord br = (BOFRecord) record;
                if (br.getType() == BOFRecord.TYPE_WORKSHEET) {
                    // 如果有需要,则建立子工作薄
                    if (workbookBuildingListener != null && stubWorkbook == null) {
                        stubWorkbook = workbookBuildingListener
                                .getStubHSSFWorkbook();
                    }
                   
                    sheetIndex++;
                    if (orderedBSRs == null) {
                        orderedBSRs = BoundSheetRecord
                                .orderByBofPosition(boundSheetRecords);
                    }
                    sheetName = orderedBSRs[sheetIndex].getSheetname();
                }
                break;
   
            ca SSTRecord.sid:
                sstRecord = (SSTRecord) record;
                break;
   
            ca BlankRecord.sid:
                BlankRecord brec = (BlankRecord) record;
                thisRow = Row();
                thisColumn = Column();
                thisStr = "";
                rowlist.add(thisColumn, thisStr);
                break;
            ca BoolErrRecord.sid: //单元格为布尔类型
                BoolErrRecord berec = (BoolErrRecord) record;
                thisRow = Row();
                thisColumn = Column();
                thisStr = BooleanValue()+"";
                rowlist.add(thisColumn, thisStr);
                break;
   
            ca FormulaRecord.sid: //单元格为公式类型
                FormulaRecord frec = (FormulaRecord) record;
                thisRow = Row();
                thisColumn = Column();
                if (outputFormulaValues) {
                    if (Double.isNaN(Value())) {
                        // Formula result is a string
                        // This is stored in the next record
                        outputNextStringRecord = true;
                        nextRow = Row();
                        nextColumn = Column();
                    } el {
                        thisStr = formatListener.formatNumberDateCell(frec);
                    }
                } el {
                    thisStr = '"' + HSSFFormulaParr.toFormulaString(stubWorkbook,
                            PardExpression()) + '"';
                }
                rowlist.add(thisColumn,thisStr);
                break;
            ca StringRecord.sid://单元格中公式的字符串
                if (outputNextStringRecord) {
                    // String for formula
                    StringRecord srec = (StringRecord) record;
                    thisStr = String();
                    thisRow = nextRow;
                    thisColumn = nextColumn;
                    outputNextStringRecord = fal;
                }
                break;
            ca LabelRecord.sid:
                LabelRecord lrec = (LabelRecord) record;
                curRow = thisRow = Row();
                thisColumn = Column();
                value = Value().trim();
                value = value.equals("")?" ":value;
                this.rowlist.add(thisColumn, value);
                break;
            ca LabelSSTRecord.sid//单元格为字符串类型
                LabelSSTRecord lsrec = (LabelSSTRecord) record;
                curRow = thisRow = Row();
                thisColumn = Column();
                if (sstRecord == null) {
                    rowlist.add(thisColumn, " ");
                } el {
                    value =  sstRecord
                    .SSTIndex()).toString().trim();
                    value = value.equals("")?" ":value;
                    入住证明rowlist.add(thisColumn,value);
                }
                break;
            ca NumberRecord.sid//单元格为数字类型
                NumberRecord numrec = (NumberRecord) record;
                curRow = thisRow = Row();
                thisColumn = Column();
                value = formatListener.formatNumberDateCell(numrec).trim();
                value = value.equals("")?" ":value;
                // 向容器加入列值
                rowlist.add(thisColumn, value);
                break;
            default:
                break;
        }
        // 遇到新行的操作
        if (thisRow != -1 && thisRow != lastRowNumber) {
            lastColumnNumber = -1;
        }
        // 空值的操作
        if (record instanceof MissingCellDummyRecord) {
            MissingCellDummyRecord mc = (MissingCellDummyRecord) record;
            curRow = thisRow = mc.getRow();
            thisColumn = mc.getColumn();
            rowlist.add(thisColumn," ");
        }
        // 更新行和列的值
        if (thisRow > -1)
            lastRowNumber = thisRow;
        if (thisColumn > -1)
            lastColumnNumber = thisColumn;
        // 行结束时的操作
        if (record instanceof LastCellOfRowDummyRecord) {
            if (minColumns > 0) {
                // 列值重新置空
                if (lastColumnNumber == -1) {
                    lastColumnNumber = 0;
                }
            }
            lastColumnNumber = -1;
                // 每行结束时, 调用getRows() 方法
            rowReader.getRows(sheetIndex,curRow, rowlist);
           
            // 清空容器
            rowlist.clear();
        }
    }
   
}
Excel2003Writer.java
package l.outofmemory;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.urmodel.HSSFWorkbook;
import org.apache.poi.ss.urmodel.Cell;
import org.apache.poi.ss.urmodel.Row;
import org.apache.poi.ss.urmodel.Sheet;
import org.apache.poi.ss.urmodel.Workbook;
public class Excel2003Writer {
    /**
    * @param args
    */
    public static void main(String[] args) {
        try{   
            System.out.println("开始写入");
            writeExcel("tes2003.xls");
            System.out.println("写完xcel2003");
        } catch (IOException e) {
       
        }
    }
   
   
    /**
    * 写入excel并填充内容,一个sheet只能写65536行以下,超出会报异常,写入时建议使用AbstractExcel2007Writer
    * @param fileName
    * @throws IOException
    */
    public static void writeExcel(String fileName) throws IOException{
           
            // 创建excel2003对象
            Workbook wb = new HSSFWorkbook();
           
            // 设置文件放置路径和文件名
            FileOutputStream fileOut = new FileOutputStream(fileName);
            // 创建新的表单
            Sheet sheet = wb.createSheet("newsheet");
            // 创建新行
            for(int i=0;i<20000;i++){
                Row row = ateRow(i);
                // 创建单元格
                Cell cell = ateCell(0);
                // 设置单元格值
                cell.tCellValue(1);
                ateCell(1).tCellValue(1+i);
                ateCell(2).tCellValue(true);
                ateCell(3).tCellValue(0.43d);
                ateCell(4).tCellValue('d');
                ateCell(5).tCellValue("");
                ateCell(6).tCellValue("第七列"+i);
                ateCell(7).tCellValue("第八列"+i);
            }
            wb.write(fileOut);
            fileOut.clo();
    }
}
Excel2007Reader.java
package l.outofmemory;
import java.io.InputStream;
import java.math.BigDecimal;
import SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.urmodel.HSSFDateUtil;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventurmodel.XSSFReader;
import org.apache.del.SharedStringsTable;
import org.apache.poi.xssf.urmodel.XSSFRichTextString;
import l.sax.Attributes;
import l.sax.InputSource;
import l.sax.SAXException;
import l.sax.XMLReader;
import l.sax.helpers.DefaultHandler;
import l.sax.helpers.XMLReaderFactory;
/**
* 抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析
* xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低
* 内存的耗费,特别使用于大数据量的文件。
*
*/
public class Excel2007Reader extends DefaultHandler {
    //共享字符串表
    private SharedStringsTable sst;
    //上一次的内容
    private String lastContents;
    private boolean nextIsString;
    private int sheetIndex = -1;
    private List<String> rowlist = new ArrayList<String>();
    //当前行
    private int curRow = 0;
    //当前列
    private int curCol = 0;
    //日期标志
    private boolean dateFlag;
    //数字标志
    private boolean numberFlag;
   
    private boolean isTElement;
   
    private IRowReader rowReader;
   
    public void tRowReader(IRowReader rowReader){
        this.rowReader = rowReader;
    }
   
    /**只遍历一个电子表格,其中sheetId为要遍历的sheet索引,从1开始,1-3
    * @param filename
    * @param sheetId
    * @throws Exception
    */
    public void processOneSheet(String filename,int sheetId) throws Exception {
        OPCPackage pkg = OPCPackage.open(filename);
        XSSFReader r = new XSSFReader(pkg);
        SharedStringsTable sst = r.getSharedStringsTable();
        XMLReader parr = fetchSheetParr(sst);
       
        // 根据 rId# rSheet# 查找sheet
        InputStream sheet2 = r.getSheet("rId"+sheetId);
        sheetIndex++;
        InputSource sheetSource = new InputSource(sheet2);
        parr.par(sheetSource);
        sheet2.clo();
    }
    /**
    * 遍历工作簿中所有的电子表格
    * @param filename
    * @throws Exception
    */
    public void process(String filename) throws Exception {
        OPCPackage pkg = OPCPackage.open(filename);
        XSSFReader r = new XSSFReader(pkg);
        SharedStringsTable sst = r.getSharedStringsTable();
        XMLReader parr = fetchSheetParr(sst);
        Iterator<InputStream> sheets = r.getSheetsData();
        while (sheets.hasNext()) {
            curRow = 0;
            sheetIndex++;
            InputStream sheet = ();
            InputSource sheetSource = new InputSource(sheet);
            parr.par(sheetSource);
            sheet.clo();
        }
    }
    public XMLReader fetchSheetParr(SharedStringsTable sst)
            throws SAXException {
        XMLReader parr = XMLReaderFactory
                .createXMLReader("s.parrs.SAXParr");
        this.sst = sst;
        parr.tContentHandler(this);
        return parr;
    }
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
       
        // c => 单元格
        if ("c".equals(name)) {
            // 如果下一个元素是 SST 的索引,则将nextIsString标记为true
            String cellType = Value("t");
            if ("s".equals(cellType)) {
                nextIsString = true朋友圈的精美句子;
            } el {
                nextIsString = fal;
            }
            //日期格式
            String cellDateType = Value("s");
            if ("1".equals(cellDateType)){
                dateFlag = true;
            } el {
                dateFlag = fal;
            }
            String cellNumberType = Value("s");
            if("2".equals(cellNumberType)){
                numberFlag = true;
            } el {
                numberFlag = fal;
            }
           
        }
        //当元素为t
        if("t".equals(name)){
            isTElement = true;
        } el {
            isTElement = fal;
        }
       
        // 置空
        lastContents = "";
    }
    public void endElement(String uri, String localName, String name)
            throws SAXException {
       
        // 根据SST的索引值的到单元格的真正要存储的字符串
        // 这时characters()方法可能会被调用多次
        if (nextIsString) {买二送一
            try {
                int idx = Integer.parInt(lastContents);
                lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
                        .toString();
            } catch (Exception e) {
            }
        }
        //t元素也包含字符串
        if(isTElement){
            String value = lastContents.trim();
            rowlist.add(curCol, value);
            curCol++;
            isTElement = fal;
            // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
            // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
        } el if ("v".equals(name)) {
            String value = lastContents.trim();
            value = value.equals("")?" ":value;
            //日期格式处理
            if(dateFlag){
                Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));
                SimpleDateFormat dateFormat = new SimpleDateFormat(
                "dd/MM/yyyy");
                value = dateFormat.format(date);
            }
            //数字类型处理
            if(numberFlag){
                BigDecimal bd = new BigDecimal(value);
                value = bd.tScale(3,BigDecimal.ROUND_UP).toString();
            }
            rowlist.add(curCol, value);
            curCol++;
        }el {
            //如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
            if (name.equals("row")) {
                rowReader.getRows(sheetIndex,curRow,rowlist);
                rowlist.clear();
                curRow++;
                curCol = 0;
            }
        }
       
    }
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        //得到单元格内容的值
        lastContents += new String(ch, start, length);
    }
}
Excel2007WriterImpl.java
package l.outofmemory;
public class Excel2007WriterImpl extends AbstractExcel2007Writer {
    /**
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("............................");
        long start = System.currentTimeMillis();
        // 构建excel2007写入器
        AbstractExcel2007Writer excel07Writer = new Excel2007WriterImpl();
        // 调用处理方法
        excel07Writer.process("F://test07.xlsx");
        long end = System.currentTimeMillis();
        System.out.println("....................." + (end - start) / 1000);
    }
    /*
    * 可根据需求重写此方法,对于单元格的小数或者日期格式,会出现精度问题或者日期格式转化问题,建议使用字符串插入方法
    *
    * @l.ver2.AbstractExcel2007Writer#generate()
    */
    @Override
    public void generate() throws Exception {
        // 电子表格开始
        beginSheet();
        for (int rownum = 0; rownum < 100; rownum++) {
            // 插入新行
            inrtRow(rownum);
            // 建立新单元格,索引值从0开始,表示第一列
            createCell(0, "中国<" + rownum + "!");
            createCell(1, 34343.123456789);
            createCell(2, "23.67%");
            createCell(3, "12:12:23");
            createCell(4, "2010-10-11 12:12:23");
            createCell(5, "true");
            createCell(6, "fal");
            // 结束行
            endRow();
        }
        // 电子表格结束
        endSheet();
    }
}
ExcelReaderUtil.java

本文发布于:2023-07-20 20:26:29,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1089571.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:文件   使用   方法   字符串   电子表格   模板
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图