deejaybee
Goto Top

Batch XML Importer - Import von Dateien mittels Batch und xml-File

Hallo zusammen,

ich habe gerade folgendes Problem:

Ein Scanner scannt Dokumente in ein Verzeichnis C:\scans\auftraege
Er benennt jedes Dokument fortlaufend Dok_001.tif
Dazu gibts jeweils eine xml-Datei Dok_001.xml, die soetwas hier enthält:

<?xml version="1.0" encoding="utf-16"?> 
<ScanIndex ScanProfilName="Auftrag erfassen" FileName="Dok_001.tif" Seitengescant="2" Seitengeloescht="1" Seitengespeichert="1"> 
    <ImagePropertys Path="C:\scans\auftraege" /> 
    <FieldsInfo>
        <feld_1 Feldtyp="Auswahl" Feldname="Jahr" Feldlaenge="4" Wert="2014" /> 
        <feld_2 Feldtyp="Text" Feldname="Auftrags-Nr." Feldlaenge="20" Wert="44545" /> 
    </FieldsInfo>
</ScanIndex>

Ich möchte folgendes tun:

Ein Batchscript, welches den Ordner C:\scans\auftraege durchsucht nach xml-Dateien.
Aus diesen xml-Dateien das zugehörige Dokument ( FileName="Dok_001.tif" ) anfasst und verschiebt.
Zielpfad soll sein: w:\auftraege\ %jahr% \ %auftragsnummer% \ Dok_001_%auftragsnummer%.tif

Das Quell xml und tif Dokument soll nach Verarbeitung aus dem Ordner scans entfernt werden.

Theoretisch eigentlich nicht schwer, nur steh ich da irgendwie aufm Schlauch.

Könnt ihr mir helfen?

Schöne Grüße
Daniel


Nachtrag:
Seitengescant="2" Seitengeloescht="1" Seitengespeichert="1"
Der Scanner kann leere Seiten entfernen, bzw auch mehrere Dateien einzeln ablegen, so dass eine xml-Datei für mehrere tifs verfügbar ist.
Das Ergebnis wären Dok_001_001 - Dok_001_%seitenanzahl%
Indikator für mehrere tif Dokumente zu einer xml ist der Parameter %seitengespeichert%
Ich werd den Scanner allerdings so einstellen, dass er zu jeder xml auch nur 1 tif-Dokument hat. Aber falls jemand auch für diesen Teil eine Lösung hat, ich wäre nicht böse.

Content-ID: 241712

Url: https://administrator.de/contentid/241712

Ausgedruckt am: 22.11.2024 um 20:11 Uhr

Friemler
Lösung Friemler 24.06.2014, aktualisiert am 27.06.2014 um 09:53:03 Uhr
Goto Top
Hallo Daniel,

da Deine XML-Datei laut Header in UTF-16 kodiert ist, eignet sich Batchscript nicht zum Parsen der Datei. MS stellt für solche Zwecke aber glücklicherweise eine ActiveX-Komponente bereit, die sich von VB Script aus nutzen lässt. Die Arbeit damit ist außerdem viel bequemer. Deshalb hier mein Vorschlag in VB Script:

Option Explicit


'Konfiguration  
Const strSourceFolder = "C:\Scans\Auftraege"  
Const strDestFolder   = "W:\Auftraege"  


'Deklaration der globalen Variablen  
Dim objFSO, objXMLDoc, objFile
Dim strFilePath, strFileName, strPageCount, strJobNum, strYear


'Objekte erzeugen  
Set objFSO    = CreateObject("Scripting.FileSystemObject")  
Set objXMLDoc = CreateObject("MSXML2.DOMDocument.6.0")  


'Hauptprogramm  
For Each objFile In objFSO.GetFolder(strSourceFolder).Files
  If StrComp(objFSO.GetExtensionName(objFile.Path), "xml", vbTextCompare) = 0 Then  
    If LoadXMLFile(objFile.Path) Then
      Call ParseXMLFile
      If ProcessTask Then Call DeleteFile(objFile.Path)
    Else
      WScript.Echo "Fehler beim Laden der Datei" & vbCrLf & _  
                   objFile.Path & vbCrLf & _
                   objXMLDoc.parseError.reason
    End If
  End If
Next



'******************** Unterprogramme *******************  

Function LoadXMLFile(strFilePath)
  objXMLDoc.async            = False
  objXMLDoc.validateOnParse  = False
  objXMLDoc.resolveExternals = False

  Call objXMLDoc.load(strFilePath)

  LoadXMLFile = (objXMLDoc.parseError.errorCode = 0)
End Function


Sub ParseXMLFile
  Dim objScanIndex, objImgProps, objFieldsInfo, objFieldInfo
  Dim strFieldName
  
  'XML-Knoten ScanIndex einlesen und Attribute auswerten  
  Set objScanIndex  = objXMLDoc.documentElement.selectSingleNode("//ScanIndex")  
  strFileName       = objScanIndex.getAttribute("FileName")  
  strPageCount      = objScanIndex.getAttribute("Seitengespeichert")  
                    
  'XML-Knoten ImagePropertys einlesen und Attribute auswerten  
  Set objImgProps   = objXMLDoc.documentElement.selectSingleNode("//ImagePropertys")  
  strFilePath       = objImgProps.getAttribute("Path")  

  'XML-Knoten FieldsInfo einlesen...  
  Set objFieldsInfo = objXMLDoc.documentElement.selectSingleNode("//FieldsInfo")  

  '...und Attribute auswerten  
  For Each objFieldInfo In objFieldsInfo.childNodes
    strFieldName = objFieldInfo.getAttribute("Feldname")  
    
    If     strFieldName = "Jahr" Then  
      strYear   = objFieldInfo.getAttribute("Wert")  
    ElseIf strFieldName = "Auftrags-Nr." Then  
      strJobNum = objFieldInfo.getAttribute("Wert")  
    End If
  Next
End Sub


Function ProcessTask
  On Error Resume Next
  
  Dim intCnt, intFileCnt
  Dim strSrcFileName, strSrcFilePath, strDstFileName, strDstFilePath
  Dim arrFiles
  
  intFileCnt = CInt(strPageCount)
  arrFiles   = Array()
  
  For intCnt = 1 To intFileCnt
    If intFileCnt = 1 Then
      strSrcFilePath = objFSO.BuildPath(strFilePath, strFileName)
      strDstFileName = objFSO.GetBaseName(strFileName) & "_" & strJobNum & "." & objFSO.GetExtensionName(strFileName)  
      strDstFilePath = objFSO.BuildPath(strDestFolder, strYear & "\" & strJobNum & "\" & strDstFileName)  
    Else
      strSrcFileName = objFSO.GetBaseName(strFileName) & "_" & Right("00" & CStr(intCnt), 3) & "." & objFSO.GetExtensionName(strFileName)  
      strSrcFilePath = objFSO.BuildPath(strFilePath, strSrcFileName)
      strDstFileName = objFSO.GetBaseName(strSrcFileName) & "_" & strJobNum & "." & objFSO.GetExtensionName(strSrcFileName)  
      strDstFilePath = objFSO.BuildPath(strDestFolder, strYear & "\" & strJobNum & "\" & strDstFileName)  
    End If
    
    ProcessTask = CopyFile(strSrcFilePath, strDstFilePath)
    If Not ProcessTask Then Exit For
    
    ReDim Preserve arrFiles(UBound(arrFiles) + 1)
    arrFiles(UBound(arrFiles)) = strSrcFilePath
  Next
  
  If ProcessTask Then Call DeleteImageFiles(arrFiles)
End Function


Function CopyFile(strSrcFilePath, strDstFilePath)
  On Error Resume Next
  
  Dim objShell, strDstPath
  
  strDstPath = objFSO.GetParentFolderName(strDstFilePath)
  
  If Not objFSO.FolderExists(strDstPath) Then
    Set objShell = CreateObject("WScript.Shell")  
    Call objShell.Run("cmd /c mkdir """ & strDstPath & """", 0, True)  
  End If
  
  Call objFSO.CopyFile(strSrcFilePath, strDstFilePath, False)
  CopyFile = (Err.Number = 0)

  Call TestError("Fehler beim Kopieren der Datei", strSrcFilePath)  
End Function


Sub DeleteImageFiles(ByRef arrFiles)
  On Error Resume Next
  
  Dim intCnt
  
  For intCnt = 0 To UBound(arrFiles)
    Call DeleteFile(arrFiles(intCnt))
  Next
End Sub


Sub DeleteFile(strFilePath)
  On Error Resume Next
  
  Call objFSO.DeleteFile(strFilePath, True)
  Call TestError("Fehler beim Löschen der Datei", strFilePath)  
End Sub


Sub TestError(strMessage, strFilePath)
  If Err.Number <> 0 Then
    WScript.Echo strMessage & vbCrLf & _
                 strFilePath & vbCrLf & _
                 Err.Description
    Err.Clear
  End If
End Sub

Den Code in eine Textdatei mit der Dateierweiterung vbs speichern. Du kannst das Script dann per Doppelklick starten.

Die Zeilen 5 und 6 musst Du ggf. anpassen. Falls sich die Namen der Knoten in der XML-Datei ändern bzw. mit Deinen Angaben nicht übereinstimmen, musst Du die entsprechenden Stellen im Unterprogramm ParseXMLFile ändern.

Gruß
Friemler
DeeJayBee
DeeJayBee 25.06.2014 um 09:44:58 Uhr
Goto Top
Hallo Friemler,

vielen Dank schon einmal!
Das ist doch etwas umfangreicher, als ich erwartet habe...

Mein erster Test wirft einen Kompilierungsfehler:

Zeile 93, Zeichen 52
Beim Aufrufen einer Unterroutine (Sub) dürfen keine Klammern verwendet werden.

Eigentlich sieht dieser Aufruf für mich okay aus...
Da meine VB-Kenntnisse noch bescheidener sind als mit Batch komm ich hier nicht weiter...


Gruß
Daniel
colinardo
Lösung colinardo 25.06.2014, aktualisiert am 27.06.2014 um 09:53:07 Uhr
Goto Top
Hallo Daniel,
Beim Aufrufen einer Unterroutine (Sub) dürfen keine Klammern verwendet werden.
schreib es so:
objFSO.MoveFile strSrcFilePath, strDstFilePath
Grüße Uwe

p.s. eine alternative Lösung mit Powershell könnte so aussehen (Funktioniert ebenfalls für beide Varianten mit einer oder mehreren Files):
#------------------------------------------
$sourcePath ="C:\scans\auftraege"   
$targetPath = "w:\auftraege"  
# -----------------------------------------
$xml = new-object XML
$files = dir "$sourcePath\*.xml"  
foreach($file in $files){
    # XML-File laden
    $xml.Load($file.FullName)
    
    # Infos extrahieren
    $strFName = $xml.ScanIndex.FileName
    $strYear = $xml.ScanIndex.FieldsInfo.SelectSingleNode("*[@Feldname='Jahr']").Wert  
    $strJobNr = $xml.ScanIndex.FieldsInfo.SelectSingleNode("*[@Feldname='Auftrags-Nr.']").Wert  
    
    # Zielordner festlegen und erzeugen
    $newFolder = "$targetPath\$strYear\$strJobNr"  
    if(!(Test-Path $newFolder)){md $newFolder -Force | out-null}

    # Files verschieben
    $finfo = [System.IO.FileInfo]"$strFName"  
    dir "$sourcePath\$($finfo.BaseName)*$($finfo.Extension)" | %{move-item $_.FullName "$newFolder\$($_.BaseName)_$strJobNr$($_.Extension)"}  
    
    # XML File löschen
    del $file.FullName -Force
}
Friemler
Friemler 25.06.2014 um 11:43:52 Uhr
Goto Top
Hallo Daniel,

sorry, man sollte seinen Code doch immer VOLLSTÄNDIG testen... face-sad

Ich habe das Script oben korrigiert.

Gruß
Friemler
DeeJayBee
DeeJayBee 26.06.2014 aktualisiert um 12:02:56 Uhr
Goto Top
Hallo ihr beiden!

Respekt erstmal, das sieht beides super aus.

@colinardo : Kannst Du mir noch sagen, wie ich das Script zum laufen bekomme?
Da Dein Script kürzer ist, sieht es übersichtlicher aus. Das würde ich auch gerne testen.
Ich weiß nur nicht, wie man Powershell am Rechner einrichtet / einrichten muss. Habe bislang nur Batch Erfahrung.

@Friemler : Das vb-Script läuft einwandfrei, ich habe noch einen kleinen Zähler mit eingebaut, der mir die Anzahl der Durchläufe anzeigt face-smile

Grüße
Daniel
colinardo
colinardo 26.06.2014, aktualisiert am 27.06.2014 um 18:22:14 Uhr
Goto Top
Zitat von @DeeJayBee:
@colinardo : Kannst Du mir noch sagen, wie ich das Script zum laufen bekomme?
back-to-topAnleitung: Wie starte ich Powershell-Scripte
  • Zuerst speicherst man den Code in einer Textdatei mit der Endung .ps1.
  • Wenn man zum ersten mal Powershell-Scripte ausführt, musst man einmalig vorher noch das Ausführen von Scripten im User-Account freischalten. Dazu öffnet man eine Powershell-Konsole und gibt dort den Befehl Set-ExecutionPolicy RemoteSigned -Force ein. Um diese Policy für alle User auf dem Rechner zu setzen muss man diesen Befehl in einer Powershell-Konsole mit Admin-Rechten starten. Noch ein Hinweis für 64-Bit-Systeme: Hier sollte sowohl für die 32bit und 64Bit Variante der Powershell die Policy in einer Admin-Konsole gesetzt werden: Set-ExecutionPolicy RemoteSigned -Force; start-job { Set-ExecutionPolicy RemoteSigned -Force } -RunAs32
  • Jetzt kann das Powershell-Script in der Konsole mit Eingabe des Pfades der Scriptdatei, oder mit einem Rechtsklick auf die Script-Datei :Mit Powershell ausführen gestartet werden.
DeeJayBee
DeeJayBee 27.06.2014 um 09:52:41 Uhr
Goto Top
Perfekt!

Vielen Dank face-smile