2014-10-29 85 views
0

有关此目标背后动机的更多详细信息,请参阅我的previous question。我(再次)决定完全问这个问题为一个新问题,因为我认为它已经发展到足以值得这样做。总之,我打算使用JDOM与NIO结合使用:Xml附加到文件结尾,而不是替换文件

  1. 获得xml文件的独占文件锁定。
  2. 将文件读取到Document对象中。
  3. 进行任意更改(锁仍处于活动状态!)。
  4. 将更改写回到xml文件。
  5. 释放文件锁定。

具有压倒一切的FilterInputStream的关闭行为解决an issue with reading the xml file via a channel,现在我有我可以写使用Transformer.transform()锁定的节目。然而,问题是不是替换原始文件,Transformer.Transform是将新文件追加到原始文件的末尾而不是替换它(请参阅图像)。

enter image description here

的问题是不与Document对象本身作为可通过打印字符串从以下方法返回,使用所述Document对象作为输入可以看出:

public String toXMLString(Node node) { 
    try { 
     this.removeBlankTextNodes(node); 
     StringWriter sw = new StringWriter(); 
     TransformerFactory tf = TransformerFactory.newInstance(); 
     Transformer transformer = tf.newTransformer(); 
     transformer.transform(new DOMSource(node), new StreamResult(sw)); 
     return sw.toString(); 
    } catch (TransformerException ex) { 
     throw new RuntimeException("Error converting to String", ex); 
    } 
} 

也不对追加当一个新频道建立到同一个文件并用作Transformer.transform()result时,会发生问题。所以,这个问题只出现在同一信道用于读取和写入(也许这就是为什么他们选择调用DocumentBuilder.parse()时自动关闭通道发生。

我已经相当彻底检查文档,找不到任何相关的选项来指定Transformer.transform的输出(我已经搜索过Transformer/Transformer factory/StreamResult),尽管由于许多类都是抽象的,我正在努力寻找实际的实现代码。看起来append选项设置为false,所以我主要怀疑是在读取操作完成后我需要以某种方式清除通道(或相关的缓冲区?)。我尝试的最后一件事是只用通道打开通道关于除“APPEND”之外使用代码channel.open(Paths.get(path), StandardOpenOption.CREATE)的每个选项;这再次没有效果。请注意,我不能关闭并重新打开频道,因为这会释放文件锁定。任何指针/建议将会很棒!代码如下:

import java.nio.channels.*; 

import javax.xml.parsers.*; 
import javax.xml.transform.*; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 
import javax.xml.xpath.*; 

import org.w3c.dom.*; 
import org.xml.sax.SAXException; 

public class Test2{ 
    String path = "...Test 2.xml"; 

    public Test2(){ 
     Document doc = null; 
     DocumentBuilderFactory dbFactory; 
     DocumentBuilder dBuilder; 
     NodeList itemList; 
     Transformer transformer; 
     FileChannel channel; 
     Element newElement; 
     int prevNumber; 
     TransformerFactory transformerFactory ; 
     DOMSource source; 
     StreamResult result; 
     NonClosingInputStream ncis = null; 
     try { 
      channel = new RandomAccessFile(new File(path), "rw").getChannel(); 
      FileLock lock = channel.lock(0L, Long.MAX_VALUE, false); 

      try { 
       dbFactory = DocumentBuilderFactory.newInstance(); 
       dBuilder = dbFactory.newDocumentBuilder(); 
       ncis = new NonClosingInputStream(Channels.newInputStream(channel)); 
       doc = dBuilder.parse(ncis); 
      } catch (SAXException | IOException | ParserConfigurationException e) { 
       e.printStackTrace(); 
      } 
      doc.getDocumentElement().normalize(); 
      itemList = doc.getElementsByTagName("Item"); 
      newElement = doc.createElement("Item"); 
      prevNumber = Integer.parseInt(((Element) itemList.item(itemList.getLength() - 1)).getAttribute("Number")); 
      newElement.setAttribute("Number", (prevNumber + 1) + ""); 
      doc.getDocumentElement().appendChild(newElement); 



      transformerFactory = TransformerFactory.newInstance(); 
      transformer = transformerFactory.newTransformer(); 
      source = new DOMSource(doc); 

      //channel.open(Paths.get(path), StandardOpenOption.CREATE); 
      result = new StreamResult(Channels.newOutputStream(channel)); 

      transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 
      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); 
      transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 
      transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
      transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
      transformer.transform(source, result); 
      channel.close(); 
     } catch (IOException | TransformerException e) { 
      e.printStackTrace(); 
     } finally { 
      try { 
       ncis.reallyClose(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    class NonClosingInputStream extends FilterInputStream { 

     public NonClosingInputStream(InputStream it) { 
      super(it); 
     } 

     @Override 
     public void close() throws IOException { 
      // Do nothing. 
     } 

     public void reallyClose() throws IOException { 
      // Actually close. 
      in.close(); 
     } 
    } 

    public static void main(String[] args){ 
     new Test2(); 
    } 
} 

回答

1

这几乎肯定发生,因为您的频道定位在文件的结尾在看完后

您应该能够通过实施之前执行以下操作来解决这个问题变换:

channel.truncate(0); 

这应该文件截断的大小的0和位置复位为0。

http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html#truncate(long)

+0

嗨,谢谢你的回复。设置通道位置确实如您所说的那样工作,所以朝着正确的方向迈出了一步。不幸的是,我不能删除频道(删除文件没有效果),而没有释放相关的文件锁定,会有一种方法来调整频道的大小吗? – Hungry 2014-10-29 10:18:16

+0

@ btrs20看起来你应该能够截断这个文件,我想应该能够解决这个问题。你可以试试吗? – JLRishe 2014-10-29 10:32:44

+0

这很完美,谢谢!我花了将近一天的时间,努力研究如何在20个字符中做一些可能的事情...... – Hungry 2014-10-29 10:38:36

相关问题