2014-10-29 87 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

相關問題