Java文件IO和“访问被拒绝”错误

9

我很苦恼于这个问题,因此我正在寻求帮助。

我有一段代码循环执行以下操作:

//imports ommitted
public void afterPropertiesSet() throws Exception{

  //building of URL list ommitted  
  // urlMap is a HashMap <String,String> created and populated just prior   

    for ( Object urlVar : urlMap.keySet() ){
    String myURLvar = urlMap.get(urlVar.toString);
    System.out.println ("URL is "+myURLvar );
    BufferedImage imageVar = ImageIO.read(myURLvar);//URL confirmed to be valid even for executions that fail
    String fileName2Save = "filepath"// a valid file path
    System.out.println ("Target path is "+fileName2Save );
    File file2Save = new File (fileName2Save);
    fileName2Save.SetWriteable(true);//set these just to be sure
    fileName2Save.SetReadable(true);
      try{
       ImageIO.write (imageVar,"png",file2save)//error thrown here 
      }catch (Exception e){
     System.out.println("R: "+file2Save.canRead()+" W: "+file2Save.canWrite()+" E:"+file2Save.canExecute()+" Exists: "+file2Save.exists+" is a file"+file2Save.isFile() );

     System.out.println("parent Directory perms");// same as above except on parent directory of destination
      }//end try
     }//end for
     }

这一切都在Windows 7和JDK 1.6.26以及Netbeans、Tomcat 7.0.14上运行。目标目录实际上位于我的netbeans项目目录中,位于普通Web应用程序的文件夹中(WEB-INF之外),我通常会期望在那里有写入文件的权限。
当出现错误时,对于文件的两个结果分别是a.)全为假 b.)全为真。父目录权限永远不会更改,除了isFile之外永远为真。
抛出的错误(java.IO.error,并带有“access denied”)并非每次都发生...事实上,循环运行60%的时间它不会抛出任何错误。剩余40%的时间,我在它写入的60多个文件中的一个上收到错误。偶尔是同一个。它开始的URL的顺序每次都会改变,因此编写文件的顺序是可变的。文件名具有简短而简洁的名称,例如“1.png”。图像很小...小于8k。
为确保权限正确,我已经:
- 给予每个人从net beans项目目录向下的“完全控制” - 以管理员身份运行JDK、JRE和Netbeans - 禁用UAC
然而错误仍然存在。对此的谷歌搜索似乎五花八门,而且通常读起来像巫术。显然,我(以及Java和Netbeans等)应该有权限将文件写入目录。
任何人有任何见解吗?这是所有内容(代码和托管URL的Web服务器)都在封闭系统上,因此我无法剪切和粘贴代码或堆栈跟踪。
更新:我通过在每次读取之前进行println和toString来确认imageURL有效。然后,我确认a.)托管目标URL的Web服务器返回了带有http 200代码的图像b.)在Web浏览器中测试时,该URL返回了图像。在测试中,我还在读取后添加了if()来确认值不为空或空。我还对所有其他值进行了Null的测试。它们总是按预期工作,即使失败也是如此。错误始终在try块内发生。目标目录在每次执行之前都是相同的。在每次执行之前,目录都是空的。
更新2:这里是其中一个堆栈跟踪(在这种情况下,file2Save的权限为R:True W:True E:True isFile:True exists:True)
    java.io.FileNotFoundException <fullFilepathhere> (Access is denied)
       at java.io.RandomAccessFile.open(Native Method)
       at java.io.RandomAccessFile.<init>(RandomAccessFile.java:212)
       at javax.imageio.stream.FileImageOutputStream.<init>(FileImageOutputStream.java:53)
       at com.sun.imageio.spi.FileImageOutputStreamSpi.createOutputStreamInstance(FileImageOutputStreamSpi.java:37)
       at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:393)
       at javax.imageio.ImageIO.write(ImageIO.java:1514)
       at myPackage.myClass.afterPropertiesSet(thisClassexample.java:204)// 204 is the line number of the ImageIO write

1
你正在写入一个已存在的文件吗?如果你在tomcat或jetty下运行,Windows会出现文件锁定问题。 - gigadot
@Shahzeb 我把堆栈跟踪记录下来并发布了它。 - stimpy
如果我理解你的问题,答案是否定的。如果fileName2save被打印出来,它看起来像这样:C:Users\myname\Documents\netbeansProjects\projectName\target\appName\resources\subFolderName\1.png。其中.\resources\subFoldername是Web应用程序根目录中的静态目录。 - stimpy
抱歉问了这么多问题,但它们只是为了检查。1. 检查 subFolderName 路径是否存在。我确定它存在,因为您已经使用它来编写其他文件,但如果您自动生成嵌套的子文件夹,则需要确保它们都已创建。2. 检查文件路径是否已存在。如果它存在且您无法编写它,则可能意味着您的代码中某处存在未关闭的文件处理程序。例如,您可能会忘记在某个地方关闭 OutputStream。 - gigadot
1
@stimpy: 我也疯了...我有理由相信问题是由ImageIO.write(...)函数引起的。以老派方式保存图像是解决问题的快速方法。在我的情况下,我决定根本不使用BufferedImage。如果你不能摆脱BufferedImage,仍然有办法将其转换为byte[]并逐块保存。 - Marsellus Wallace
显示剩余10条评论
2个回答

1

这可能无法解决您的问题,因为根据您提供的有限信息,还有许多其他可能性。

在 Web 应用程序中无法写入文件的一个常见原因是 Windows 上的文件锁定问题,如果同时满足以下四个条件:

  1. 目标文件存在于 Web 根目录下,例如 WEB-INF 文件夹;
  2. 目标文件由默认 servlet 提供服务;
  3. 客户端已经至少一次请求了目标文件;
  4. 您正在运行 Windows 操作系统。

如果您尝试替换符合上述四个条件的文件,则无法进行替换,因为某些 Servlet 容器(如 Tomcat 和 Jetty)将缓冲静态内容并锁定文件,因此您无法替换或更改它们。

如果您的 Web 应用程序正好遇到此问题,则不应使用默认 servlet 来提供文件内容。默认 servlet 旨在提供您不想更改的静态内容,例如 CSS 文件、JavaScript 文件、背景图像等。

在Windows上,有一个技巧可以解决Jetty的文件锁定问题,即通过禁用NIO http://docs.codehaus.org/display/JETTY/Files+locked+on+Windows

这个技巧对于开发过程非常有用,例如您想编辑CSS文件并在不重启Web应用程序的情况下查看更改,但不建议在生产模式下使用。如果您的Web应用程序在生产过程中依赖于此技巧,则应认真考虑重新设计代码。


不错。我之前不知道这个,但是我认为在这种情况下条件3不成立(测试期间没有客户端连接到该服务器),同时条件2也很可能不成立(应用程序命名为servlet而不是默认值)。但还是感谢你提供了一个好的答案。 - stimpy
然而,这只适用于“在 Windows 上运行 Tomcat”这个特定条件。在 Linux 上运行相同的代码没有产生任何错误。打开 Tomcat 的反资源锁定功能减少了问题的发生频率(但这并不是真正的解决方案)。最终,避免此问题的唯一方法是不要在 Windows 下运行(已经测试过 Windows 7 和 Server 2008)。 - stimpy
Gevorg,你在Windows下运行Tomcat时遇到问题了吗?在我的情况下,这被证明是“解决方案”。 - stimpy
我的程序是一个定时的独立应用程序。我在Windows 7 64位上运行它时遇到了问题。我还没有在Linux上尝试过,而且我可能不会这样做,因为在弄清楚我在另一个答案中描述的解决方法之前,我已经完全改变了我的方法。 - Marsellus Wallace

1

我不能告诉你到底发生了什么或者为什么会这样... 我有一种感觉,这可能与 ImageIO 尝试保存图像的方式有关。你可以按照下面所描述的使用 ByteArrayOutputStream 来保存 BufferedImage

BufferedImage bufferedImage = ImageIO.read(new File("sample_image.gif"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "gif", baos );

baos.flush(); //Is this necessary??
byte[] resultImageAsRawBytes = baos.toByteArray();
baos.close(); //Not sure how important this is...

OutputStream out = new FileOutputStream("myImageFile.gif");
out.write(resultImageAsRawBytes);    
out.close();

我对ByteArrayOutputStream不是很熟悉,但我猜它的reset()函数在处理保存多个文件时可能会很方便。如果您喜欢,也可以尝试使用它的writeTo(OutputStream out)。文档在这里

让我知道进展如何...


我会尝试一下,看看它是否改变了事情。感谢您的输入。 - stimpy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接