【问题标题】:Applet processing a file works locally but fails in website处理文件的小程序在本地工作,但在网站上失败
【发布时间】:2012-12-18 11:18:04
【问题描述】:

这几天我一直在努力解决这个问题。我已经完成了我的全部谷歌搜索,我希望我能在这里找到比我更有经验的人(不难找到哈哈)来破解我的问题。

场景:我开发了一个实现 Swing GUI 的 Java Applet。背景工作:Applet 从大型“电话簿”excel 文件 (.csv) 中收集记录,并将它们存储在 Map 数据结构中。电话簿包含大约 106,000 条记录,在第 34,586 条记录中,我得到了一个我无法理解的 ArrayIndexOutOfBoundsException。 只有在我的个人网站上运行小程序时才会出现异常。在我的 IDE (NetBeans) 中进行测试并在我的本地计算机上运行 .html 文件(包含该小程序的文件)时,小程序运行得非常好,没有错误。在我的网站上运行时抛出的输出和异常如下(为了节省空间,我剪掉了大部分记录):

Java 控制台

Kary,Webber,2826 East 12th Ave.,Memphis,TN,38168,901-749-1834
Erinn,Rocha,2132 East Main Ave.,Memphis,TN,38168,865-414-5105
Gina,Lane,71 South First St. Apt. 11,Memphis,TN,38168,731-485-1129
Patsy,Hart,661 East 11th St.
java.lang.ArrayIndexOutOfBoundsException: 3
at Implementation.PersonnelDatabase.addRecordFromFields(PersonnelDatabase.java:192)
at Implementation.PersonnelDatabase.initDBFromFile(PersonnelDatabase.java:215)
at Implementation.PersonnelDatabase.processData(PersonnelDatabase.java:239)
at Implementation.PersonnelDatabaseApplet$2.doInBackground(PersonnelDatabaseApplet.java:78)
at Implementation.PersonnelDatabaseApplet$2.doInBackground(PersonnelDatabaseApplet.java:69)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

如您所见,在第 34,586 条记录中(从 Patsy、Hart 开始),它在她的地址中途输出。完整记录如下:Patsy,Hart,661 East 11th St. Apt. 195,孟菲斯,田纳西州,38168,555-555-5555。

以下是逻辑上受异常影响最大的代码部分。

HTML 文件中的对象标签

<object type="application/x-java-applet" height="400" width="300">
  <param name="codebase" value="classes" />
  <param name="code" value="Implementation/PersonnelDatabaseApplet.class" />
  <param name="archive" value="PersonnelDatabase.jar" />
  Applet failed to run. No Java plug-in was found.
</object>

PersonnelDatabase 类(处理后台数据):

/*
 * Create a new record using an appropriately ordered set of fields and add it to the data base
 */
public void addRecordFromFields(String[] fields)
{
    // Read record attributes in, one at a time
    Record thisRecord = new Record();
    thisRecord.setFirstName(fields[0]);
    thisRecord.setLastName(fields[1]);
    thisRecord.setAddress(fields[2]);
    thisRecord.setCity(fields[3]);
    thisRecord.setState(fields[4]);
    thisRecord.setZipCode(fields[5]);
    thisRecord.setPhoneNo(fields[6]);
    addRecord(thisRecord);
}

// O( n )
/**
 * Destroy the current data base and load new data from a file.
 * @param filename the file to use as a source
 * @throws IOException: Either file not found or IO error
 */
public void initDBFromFile(URL url) throws IOException
{
    // Open and read the file
    InputStream in = url.openStream();
    BufferedReader filein = new BufferedReader(new InputStreamReader(in));
    // Read record file, parse lines, and add records to data base
    String line = filein.readLine();
    while(line != null) {
        System.err.println(line);
        String[] fields = line.split(",");
        addRecordFromFields(fields);
        line = filein.readLine();
    }
    filein.close();
}

/**
 * Loads the default library and provides for interaction with the data
 * via the JPanel GUI inputs.
 * @param args
 * @throws IOException 
 */
public String processData(String input, int selection, URL url)
{
    //Create the main library object
    PersonnelDatabase dbiLib = new PersonnelDatabase();
    System.err.println(url);
    // Try to read the default library
    try
    {
        dbiLib.initDBFromFile(url);
    }
    catch (IOException e)
    {
        System.err.println("File IO Error");
        e.printStackTrace();
        System.exit(1);
    }
    // Queries can be simulated by typing into the console in Eclipse, and using Ctrl-d (Ctrl-z in Windows) when finished.
    // For example: "searchLastName,Smith" would print a list of all people with the last name of Smith.
    Iterable<Record> result = null;
    String[] fields = new String[2];
    if (input.contains(",")) {
        fields = input.split(",");
    }

    switch(selection) {
        case 0: result = dbiLib.searchByFirstName(input); break;
        case 1: result = dbiLib.searchByLastName(input); break;
        case 2: result = dbiLib.searchByFullName(fields[0].trim(), fields[1].trim()); break;
        case 3: result = dbiLib.searchByCity(input); break;
        case 4: result = dbiLib.searchByState(input); break;
        case 5: result = dbiLib.searchByCityState(fields[0].trim(), fields[1].trim()); break;
        case 6: result = dbiLib.searchByZip(input); break;
        case 7: result = dbiLib.searchByPhoneNumber(input); break;
        case 8: String[] newFields = new String[fields.length-1];
                System.arraycopy(fields, 1, newFields, 0, fields.length-1);
                dbiLib.addRecordFromFields(newFields);
                return "Record added successfully!\nEnter a query or add another record.";
        default: return "Invalid query.\nEnter another query or add a record.";
    }

PersonnelDatabaseApplet 类(初始化 GUI、收集输入并显示输出):

public void init() {
    /* Create and display the applet */
    try {
        java.awt.EventQueue.invokeAndWait(new Runnable() {

            @Override
            public void run() {
                initComponents();
            }
        });
    } catch (Exception e) {
        System.err.println("Creation of GUI did not successfully complete.");
    }
}

// Process inputs in the background.
SwingWorker worker = new SwingWorker<String, Void>() {
    @Override
    public String doInBackground() {
        URL url = null;
        try {
            url = new URL(getCodeBase(), fileToRead);
        }
        catch(MalformedURLException e){}
        personnelDatabase = new PersonnelDatabase();
        final String output = personnelDatabase.processData(input, selection, url);
        return output;
    }

    @Override
    public void done() {
        processingLabel.setVisible(true);
        try {
            textToDisplay = get(15, TimeUnit.SECONDS);
        } catch (InterruptedException ignore) {
            ignore.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            String why = null;
            Throwable cause = e.getCause();
            if(cause != null) {
                why = cause.getMessage();
                cause.printStackTrace();
            } else {
                why = e.getMessage();
                e.printStackTrace();
            }
            System.err.println("Error retrieving request: " + why);
            }
        if(worker.isDone() && textToDisplay != null) {
            processingLabel.setVisible(false);
            outputTextArea.setText(textToDisplay);
        }
    }
};

private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
selection = searchComboBox.getSelectedIndex();
input = valueTextField.getText();
processingLabel.setVisible(true);

worker.execute();

}

链接到我个人网站上的小程序: http://www.ryan-taylor.me/Applied%20Maps/build/PersonnelDatabaseApplet.html

我相当肯定该错误与 excel 数据本身无关,因为该程序在 NetBeans 中运行良好,并且在我的本地计算机上运行 html 时。我猜它与 Swing(线程)有关,但我不确定。我通过使用 SwingWorker 进行了更改以帮助在 Swing 线程之间传输数据,但我没有运气。我想总有可能我在实现它时错过了一些东西。

我也考虑过签署 jar,但我正在处理的文件是在线存储的 - 不是本地机器 - 所以我没有看到真正的需要。

如果有人有任何建议,我将不胜感激!

【问题讨论】:

  • 1) “Java Applet - 不喜欢 Web” 这更有意义,因为 “Web - 不喜欢 Java Applet” 一个富客户端(嵌入在瘦客户端 (HTML) 中的小程序)在最好的情况下是一种奇怪的组合。 2) 为了尽快获得更好的帮助,请发布SSCCE。对数据进行硬编码,除非这是问题所在。 3)而不是对象元素,最安全的是使用deployJava.js
  • 这些东西是不安全的、不推荐使用的、沉重的、依赖的。只是说。
  • 4) name="code" value="Implementation/PersonnelDatabaseApplet.class" 应该是 value="Implementation.PersonnelDatabaseApplet"。它是完全限定的类名,而不是文件名或相对路径。
  • 感谢大家的意见。我知道现在像这样的 Applet 是个笑话,但我并没有专业地使用它。我只是为自己使用而做。
  • 请告诉我们这不是真实数据。

标签: java swing applet swingworker indexoutofboundsexception


【解决方案1】:

看起来超出范围的数组索引是3,因为输入行仅包含三个字段,而您试图访问第四个字段(索引 3)而不检查它是否确实存在。错误在

thisRecord.setCity(fields[3]);

因为数组fields 只有三个元素。在

    String[] fields = line.split(",");
    addRecordFromFields(fields);

当你到达时

Patsy,Hart,661 East 11th St.

数组 fields 将被创建,只有 3 个条目。

如果字段数预计保持不变,那么您应该拒绝没有正确字段数的输入行。如果字段的数量可以变化,那么您必须检查返回的实际数量并仅提取实际存在的那些元素。

【讨论】:

  • Jim Garrison,完整记录如下:Patsy,Hart,661 East 11th St. Apt. 195,Memphis,TN,38168,555-555-5555 问题是 Applet 出于某种原因停止读取其余记录。另一半记录存在于 Excel 工作表中,但只是没有得到处理。
  • 所以你真的有两个问题。首先是理解ArrayIndexOutOfBoundsException,我的回答解决了这个问题。第二个是输入似乎被截断的原因。最简单的可能性是失败的输入记录实际上在第三个字段的末尾包含一个换行符。您能否在文本编辑器中打开 CSV 文件并确认该行不包含换行符?
【解决方案2】:

当您在浏览器中运行小程序时,似乎某些原因导致文件被截断。我的猜测是您正在从 Web 服务器获取文件,并且服务器或浏览器都在默默地执行一些下载限制。 (或者可能是文件在您上传时被截断...)

【讨论】:

  • 或者错误的数据字节被错误地解释为 end-of-file 在平台相关的方式中。
  • 我觉得自己像个白痴。当然会是这么简单的事情。将数据文件上传到服务器后,我的 WiFi 连接一定会中断一段时间,取消了部分上传。男孩,我是否试图使这个或什么过于复杂。感谢斯蒂芬和其他所有人的帮助!
  • @trashgod - 这在理论上是可能的。但是,任何将“坏”数据字节(在字节流中!)解释为 EOF 的平台都非常糟糕...... IMO。
【解决方案3】:

我怀疑您的 worker 和另一个因网络延迟而暴露的线程之间存在数据竞争。有两件事可能需要更仔细的审查:

  1. 使用invokeAndWait()初始化GUI是合理的,只要不对initial thread做进一步处理即可。

  2. 您的worker 不调用publish() 是不寻常的,作为process() 在事件调度线程上记录example 的一种方式。

【讨论】:

  • @thrashgod,我不需要随时随地发布背景数据(记录)。我只是将每条记录连接到一个“结果”字符串,然后将该单个值(在完整搜索完成后)返回给事件调度线程。
  • @Ryan:听起来很合理;您也可以setProgress() 让任何PropertyChangeListener 知道事情的进展。
猜你喜欢
  • 1970-01-01
  • 2018-04-15
  • 1970-01-01
  • 2017-03-04
  • 2013-03-16
  • 1970-01-01
  • 2023-03-24
  • 2012-04-09
  • 2019-05-12
相关资源
最近更新 更多