简体   繁体   English

具有依赖关系的 Maven.jar 文件将无法在除制作它的设备之外的任何其他设备上运行

[英]Maven .jar file with dependencies won't work on any other device besides the device it was made on

It is really weird, I'm making a flyermaker for my company, they need a mass scale of flyers.真的很奇怪,我正在为我的公司制作传单,他们需要大量的传单。 I am using the buildin ImageIO from java, also the iTextpdf (newest version) with the apache POI.我正在使用 java 的内置 ImageIO,以及带有 apache POI 的 iTextpdf(最新版本)。 I will put my pom.xml below.我将把我的 pom.xml 放在下面。

On both a windows 8 and windows 10 the application won't work.在 windows 8 和 windows 10 上,应用程序将无法运行。 I will provide all the code of my program below.我将在下面提供我的程序的所有代码。 As soon as i press make flyers there is nothing happening in the background.一旦我按下制作传单,背景中就没有任何事情发生。 I don't get why.我不明白为什么。

I am building my project with the following mvn statement: clean compile assembly:single我正在使用以下 mvn 语句构建我的项目:clean compile assembly:single

I am using Netbeans 11.3 and Java 8我正在使用 Netbeans 11.3 和 Java 8

POM.xml: POM.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.houseoftyping</groupId>
    <artifactId>FlyerMaker</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.houseoftyping.flyermaker.main.Start</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>unknown-jars-temp-repo</id>
            <name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace the dependencies in this repository with correct ones and delete this repository.</name>
            <url>file:${project.basedir}/lib</url>
        </repository>
    </repositories>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.6</version>
        </dependency>

    </dependencies>
</project>

Start.java (main class) I have removed all the variables, JButton's JPanes etc. to save characters. Start.java(主类) 我已经删除了所有变量,JButton 的 JPanes 等以保存字符。 These are autogenerated so nothing is wrong with them.这些是自动生成的,因此它们没有任何问题。

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.houseoftyping.flyermaker.main;

import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.IIOException;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.json.simple.parser.ParseException;

/**
 *
 * @author Lorenzo
 */
public class Start extends javax.swing.JFrame {

    /**
     * Creates new form selectExcel
     */
    public Start() {
        initComponents();
    }                     

    private void selectExcelFileActionPerformed(java.awt.event.ActionEvent evt) {                                                
        JFileChooser chooser = new JFileChooser();
        chooser.setAcceptAllFileFilterUsed(false);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.setDialogTitle("Kies een excel bestand");
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File currentFile = chooser.getSelectedFile();
            excelFileLocation.setText(String.valueOf(currentFile));
        } else {
            System.out.println("No Selection.... try again");
        }
    }                                               

    private void selectFolderActionPerformed(java.awt.event.ActionEvent evt) {                                             
        JFileChooser chooser = new JFileChooser();
        chooser.setAcceptAllFileFilterUsed(false);
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.setDialogTitle("Choose a folder");
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File currentFolder = chooser.getSelectedFile();
            folderLocation.setText(String.valueOf(currentFolder));
        } else {
            System.out.println("No Selection.... try again");
        }
    }                                            

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        if (excelFileLocation.getText() == "") {
            JOptionPane.showMessageDialog(null, "U heeft geen excel bestand gekozen, kies eerst een excel bestand.", "Geen excel bestand gekozen.", JOptionPane.ERROR_MESSAGE);
        } else if (folderLocation.getText() == "") {
            JOptionPane.showMessageDialog(null, "U heeft geen eindmap gekozen voor de gemaakte flyers, kies eerst een eindmap.", "Geen map gekozen.", JOptionPane.ERROR_MESSAGE);
        } else if (logoFolderLocation.getText() == "") {
            JOptionPane.showMessageDialog(null, "U heeft map gekozen met de schoollogo's, kies eerst een map.", "Geen map gekozen.", JOptionPane.ERROR_MESSAGE);
        } else {
            try {
                jButton1.setBackground(Color.red);
                jButton1.setEnabled(false);
                JOptionPane.showMessageDialog(null, "", "Flyers worden gemaakt!", JOptionPane.INFORMATION_MESSAGE);
                Excel excel = new Excel();
                if (!excel.readExcel(new File(excelFileLocation.getText()), new File(this.folderLocation.getText()), new File(logoFolderLocation.getText()))) {
                    JOptionPane.showMessageDialog(null, "Het gekozen excel bestand verdoet niet aan de eisen. Voor de juiste eisen tik op Help.", "Excel bestand voldoet niet aan eisen.", JOptionPane.ERROR_MESSAGE);
                    resetButton(jButton1);
                }
            } catch (IIOException ex) {
                JOptionPane.showMessageDialog(null, "Het logo wat in het excel bestand staat kan niet worden gevonden. Kijk of de juiste naam in het excel bestand staat en de juiste type.", "Fout bij plaatsen van logo", JOptionPane.ERROR_MESSAGE);
                resetButton(jButton1);
            } catch (InvalidFormatException ex) {
                resetButton(jButton1);
            } catch (ParseException ex) {
                resetButton(jButton1);
            } catch (IndexOutOfBoundsException ex) {
                JOptionPane.showMessageDialog(null, "Alle flyers zijn succesvol gemaakt!", "Succes!", JOptionPane.PLAIN_MESSAGE);
                resetButton(jButton1);
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Het excel bestand wat u wilt gebruiken kan niet worden gevonden, of staat nog open in een ander venster.", "Fout bij uitlezen van Excel bestand", JOptionPane.ERROR_MESSAGE);
                resetButton(jButton1);
            }
        }
    }                                        

    private void selectFolder2ActionPerformed(java.awt.event.ActionEvent evt) {                                              
        JFileChooser chooser = new JFileChooser();
        chooser.setAcceptAllFileFilterUsed(false);
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.setDialogTitle("Choose a folder");
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File currentFolder = chooser.getSelectedFile();
            logoFolderLocation.setText(String.valueOf(currentFolder));
        } else {
            System.out.println("No Selection.... try again");
        }
    }                                             

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) throws FileNotFoundException {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Windows".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Start.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Start.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Start.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Start.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Start().setVisible(true);
            }
        });
    }

    public void resetButton(JButton button) {
        button.setBackground(new java.awt.Color(138, 204, 234));
        button.setEnabled(true);
    }        
}

Excel.java: Excel.java:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.houseoftyping.flyermaker.main;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.json.simple.parser.ParseException;

/**
 *
 * @author Lorenzo
 */
public class Excel {

    File outputFolder;
    File logoFolder;

    public Boolean readExcel(File excelFile, File output, File logoLocation) throws IOException, InvalidFormatException, ParseException {
        this.outputFolder = output;
        this.logoFolder = logoLocation;
        // Retrieving the number of sheets in the Workbook
        try ( // Creating a Workbook from an Excel file (.xls or .xlsx)
                Workbook workbook = WorkbookFactory.create(excelFile)) {
            // Retrieving the number of sheets in the Workbook
            System.out.println("Workbook has " + workbook.getNumberOfSheets() + " Sheets : ");
            // 2. Or you can use a for-each loop
            for (Sheet sheet : workbook) {
                if (sheet.getSheetName().equals("secundair flyer")) {
                    System.out.println("Secundair flyer geselecteerd");
                    createFlyersFromExcel(sheet);
                }
            }
        }
        return null;
    }

    public void createFlyersFromExcel(Sheet sheet) throws IOException, ParseException {
        DataFormatter dataFormatter = new DataFormatter();
        boolean firstRow = false;
        for (Row row : sheet) {
            if (firstRow == false) {
                int count = 0;
                firstRow = true;
            } else {
                if (!dataFormatter.formatCellValue(row.getCell(0)).equals("0")) {
                    List<String> values = new ArrayList<>();
                    for (Cell cell : row) {
                        cell.setCellFormula(null);
                        String cellValue = dataFormatter.formatCellValue(cell);
                        if (!cellValue.equals("0")) {
                            values.add(cellValue);
                        } else {
                            values.add(null);
                        }
                    }
                    if (values.get(1) == null) {
                    } else {
                        School s = new School(values, logoFolder);
                        if (s.getLocationLogo() != null) {
                            Flyer flyer = new Flyer(s, outputFolder);
                        }
                    }
                }
            }
        }
    }
}

Flyer.java: This is where the flyer is made. Flyer.java:这是制作传单的地方。 Please ignore the hardcoded text, that is a problem for later to solve.请忽略硬编码文本,这是以后要解决的问题。

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.houseoftyping.flyermaker.main;

import com.itextpdf.text.DocumentException;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Lorenzo
 */
public class Flyer {

    private BufferedImage page1;
    private BufferedImage page2;
    private School s;

    public Flyer(School school, File outputFolder) throws IOException {
        s = school;
        if (school.getFlyerType() == null) {

        } else {
            setCorrectPagesByType();
            fillFlyer();
            finishFlyer(outputFolder);
        }
    }

    private void setCorrectPagesByType() throws IOException {
        try {
            page2 = ImageIO.read(new File("C://Users/Lorenzo/Desktop/house-of-typing-inside.jpg"));
            if (s.getFlyerType().equals("1")) {
                page1 = ImageIO.read(new File("C://Users/Lorenzo/Desktop/house-of-typing-outside-1.jpg"));
            } else if (s.getFlyerType().equals("2")) {
                page1 = ImageIO.read(new File("C://Users/Lorenzo/Desktop/house-of-typing-outside-2.jpg"));
            } else if (s.getFlyerType().equals("3")) {
                page1 = ImageIO.read(new File("C://Users/Lorenzo/Desktop/house-of-typing-outside-3.jpg"));
            } else {
                page1 = ImageIO.read(new File("C://Users/Lorenzo/Desktop/house-of-typing-outside-4.jpg"));
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    public void fillFlyer() throws IOException {
        handleSchool();
        handleType();
    }

    private void handleSchool() throws IOException {
        //Course code (only at design 1)
        if (s.getFlyerType().equals("1")) {
            //renders text
        } else if (s.getFlyerType().equals("2")) {
            //renders text
        } else if (s.getFlyerType().equals("3")) {
            //renders text
        } else {
            //renders text
        }
        //renders text

        if (s.getFlyerType().equals("2")) {
            //renders text
        }
        if (s.getFlyerType().equals("1") || s.getFlyerType().equals("2")) {
            //renders text
        } else if (s.getFlyerType().equals("3")) {
            //renders text
        } else {
            //renders text
        }
    }

    private void handleType() {
        if (s.getFlyerType().equals("1")) {
            //renders text

        } else if (s.getFlyerType().equals("2")) {
            //renders text

        } else if (s.getFlyerType().equals("3")) {
            //renders text

        } else if (s.getFlyerType().equals("4")) {
            //renders text
        }
    }

    public void renderText(int page, String text, String font, int fontSize, Color color, int width, int height) {
        final Graphics2D g;
        if (page == 1) {
            g = (Graphics2D) page1.getGraphics();
            g.drawImage(page1, 0, 0, null);
        } else {
            g = (Graphics2D) page2.getGraphics();
            g.drawImage(page2, 0, 0, null);
        }
        g.setFont(new Font(font, Font.PLAIN, fontSize));
        g.setColor(color);
        for (String txt : text.split("\n")) {
            g.drawString(txt, width, height += g.getFontMetrics().getHeight());
        }
        g.dispose();
    }

    public void renderImage(int page, File image, int width, int height, double scale, boolean mainLogo) throws IOException {
        final Graphics2D g;
        if (page == 1) {
            g = (Graphics2D) page1.getGraphics();
            g.drawImage(page1, 0, 0, null);
        } else {
            g = (Graphics2D) page2.getGraphics();
            g.drawImage(page2, 0, 0, null);
        }
        BufferedImage biImage = ImageIO.read(image);
        final AlphaComposite alphaChannel = AlphaComposite.getInstance(3, 1.0f);
        g.setComposite(alphaChannel);
            if (mainLogo) {
                while (biImage.getHeight() < 250) {
                    biImage = resize(biImage.getWidth() * 2, biImage.getHeight() * 2, biImage);
                }
                while (biImage.getHeight() > 500) {
                    biImage = resize(biImage.getWidth() / 1.2, biImage.getHeight() / 1.2, biImage);
                }
            } else {
                while (biImage.getHeight() < 150) {
                    biImage = resize(biImage.getWidth() * 2, biImage.getHeight() * 2, biImage);
                }
                while (biImage.getHeight() > 300) {
                    biImage = resize(biImage.getWidth() / 1.2, biImage.getHeight() / 1.2, biImage);
                }
            }
        g.drawImage(biImage, width, height, null);
        g.dispose();
    }

    public static BufferedImage resize(double targetWidth, double targetHeight,
            BufferedImage src) {
        double scaleW = (double) targetWidth / (double) src.getWidth();
        double scaleH = (double) targetHeight / (double) src.getHeight();

        double scale = scaleW < scaleH ? scaleW : scaleH;

        BufferedImage result = new BufferedImage((int) (src.getWidth() * scale),
                (int) (src.getHeight() * scale), BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2d = result.createGraphics();
        g2d.drawImage(src, 0, 0, result.getWidth(), result.getHeight(), null);
        g2d.dispose();

        return result;
    }

    public void renderTiltedText(int page, String text, String font, int fontSize, Color color, int width, int height, double angle) {
        final Graphics2D g;
        if (page == 1) {
            g = (Graphics2D) page1.getGraphics();
            g.drawImage(page1, 0, 0, null);
        } else {
            g = (Graphics2D) page2.getGraphics();
            g.drawImage(page2, 0, 0, null);
        }
        g.setFont(new Font(font, Font.PLAIN, fontSize));
        g.setColor(color);

        g.translate((float) 0, (float) 0);
        g.rotate(Math.toRadians(angle));

        for (String txt : text.split("\n")) {
            g.drawString(txt, width, height += g.getFontMetrics().getHeight());
        }

        g.rotate(-Math.toRadians(angle));
        g.translate(-(float) 0, -(float) 0);

        g.dispose();
    }

    public ArrayList<BufferedImage> rotateFlyer() {
        ArrayList<BufferedImage> pages = new ArrayList<>();
        pages.add(page1);
        pages.add(page2);
        ArrayList<BufferedImage> rotatedPages = new ArrayList<>();

        for (BufferedImage image : pages) {
            final double rads = Math.toRadians(90);
            final double sin = Math.abs(Math.sin(rads));
            final double cos = Math.abs(Math.cos(rads));
            final int w = (int) Math.floor(image.getWidth() * cos + image.getHeight() * sin);
            final int h = (int) Math.floor(image.getHeight() * cos + image.getWidth() * sin);
            final BufferedImage rotatedImage = new BufferedImage(w, h, image.getType());
            final AffineTransform at = new AffineTransform();
            at.translate(w / 2, h / 2);
            at.rotate(rads, 0, 0);
            at.translate(-image.getWidth() / 2, -image.getHeight() / 2);
            final AffineTransformOp rotateOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
            rotateOp.filter(image, rotatedImage);
            rotatedPages.add(rotatedImage);
        }
        return rotatedPages;
    }

    public void finishFlyer(File outputFile) throws IOException {
        ArrayList<BufferedImage> pages = rotateFlyer();
        File firstPage = new File(outputFile + "\\" + s.getSchoolName() + "-" + 1 + ".jpg");
        ImageIO.write(pages.get(0), "png", firstPage);
        File secondPage = new File(outputFile + "\\" + s.getSchoolName() + "-" + 2 + ".jpg");
        ImageIO.write(pages.get(1), "png", secondPage);
        try {
            PDF pdf = new PDF(firstPage, secondPage, outputFile, s.getSchoolName());
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Flyer.class.getName()).log(Level.SEVERE, null, ex);
        } catch (DocumentException ex) {
            Logger.getLogger(Flyer.class.getName()).log(Level.SEVERE, null, ex);
        }
        firstPage.delete();
        secondPage.delete();
        System.out.println("finished flyer of " + s.getSchoolName());
    }
}

PDF.java: PDF.java:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.houseoftyping.flyermaker.main;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Lorenzo
 */
public class PDF {

    public PDF(File image1, File image2, File root, String schoolName) throws FileNotFoundException, DocumentException, BadElementException, IOException {
        List<File> files = new ArrayList<File>();
        files.add(image1);
        files.add(image2);
        Document document = new Document();
        PdfWriter.getInstance(document, new FileOutputStream(new File(root, schoolName + ".pdf")));
        document.open();
        for (File f : files) {
            document.newPage();
            Image image;
            image = Image.getInstance(f.getAbsolutePath());
            image.setAbsolutePosition(0, 0);
            image.setBorderWidth(0);
            image.scaleAbsoluteHeight(PageSize.A4.getHeight());
            image.scaleAbsoluteWidth(PageSize.A4.getWidth());
            document.add(image);
        }
        document.close();
    }
}

School.java is basicly only setters for the values. School.java 基本上只是值的设置器。

You should modify the maven-assmebly plugin section as below to bind to the packaging phase.您应该如下修改 maven-assmebly 插件部分以绑定到打包阶段。

<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
      <archive>
          <manifest>
              <mainClass>com.houseoftyping.flyermaker.main.Start</mainClass>
          </manifest>
      </archive>
      <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
  </configuration>
  <executions>
    <execution>
      <id>make-assembly</id> 
      <phase>package</phase> 
      <goals>
        <goal>single</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Also, it is always a good idea to review the generated jar with jar tf command or a simple unzip utility to confirm that it is built with all dependency and correct entry in the Manifest file.此外,使用 jar tf 命令或简单的解压缩实用程序检查生成的 jar 始终是一个好主意,以确认它是使用 Manifest 文件中的所有依赖项和正确条目构建的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM