Frage

Übersicht

Ich habe ein Microsoft Word-Add-In, in VBA (Visual Basic für Applikationen) geschrieben, die ein Dokument und alle es im Zusammenhang Inhalt (eingebettete Medien) in ein ZIP-Archiv komprimiert. Nach dem Erstellen der Zip-Archiv stellt sie dann die Datei in einen Byte-Array ein und trägt ihn auf einen ASMX Web-Service. Diese meist funktioniert.

Issues

Das Hauptproblem ich habe, ist die Übertragung großer Dateien auf die Website. Ich kann erfolgreich eine Datei laden, die sich um 40MB, aber nicht eines, das ist 140MB (Timeout / allgemeine Fehler).

Ein zweites Problem ist, dass in dem VBScript Word den Byte-Array Aufbau Add-In, indem Sie aus dem Speicher auf dem Client-Rechner kann fehlschlagen, wenn das Zip-Archiv zu groß ist.

Mögliche Lösungen

Ich betrachte die folgenden Optionen und bin auf der Suche nach Feedback auf beiden Optionen oder anderen Vorschläge.

Option One

Öffnen einer Datei-Stream auf dem Client (MS Word VBA) und ein „Brocken“ zu einem Zeitpunkt, zu lesen und die Übertragung auf ASMX Web-Service, der auf dem Server die „Brocken“ in einer Datei zusammenfügt.

Dies hat den Vorteil, keine zusätzliche Abhängigkeiten oder Komponenten für die Anwendung hinzufügen, würde ich nur vorhandene Funktionalität werden zu modifizieren. (Weniger Abhängigkeiten sind besser als diese Lösung in einer Vielzahl von Server-Umgebungen funktionieren soll und relativ einfach einzurichten.)

Frage:

  • Gibt es Beispiele für diese oder irgendwelche empfohlenen Techniken tun (entweder auf dem Client in VBA oder in dem Web-Dienst in C # / VB.NET)?

Option Zwei

Ich verstehe WCF durch große Dateien übertragen „Chunking“ oder Streaming-Daten, die eine Lösung für das Problem zur Verfügung stellen kann. Aber ich bin nicht sehr vertraut mit WCF, und bin nicht sicher, was genau ist es in der Lage, oder wenn ich mit einem WCF-Dienst von VBA kommunizieren kann. Dies hat den Nachteil das Hinzufügen eines weiteren Abhängigkeiten (.NET 3.0). Aber wenn WCF ist auf jeden Fall eine bessere Lösung, die ich nicht daran, dass die Abhängigkeit nehmen.

Fragen:

  • Ist WCF zuverlässig die Übertragung großer Dateien dieser Art unterstützen? Wenn ja, was mit sich bringt das? Alle Ressourcen, oder Beispiele?
  • Sind Sie in der Lage einen WCF-Dienst von VBA zu nennen? Alle Beispiele?
War es hilfreich?

Lösung

ich am Ende eine Umsetzung Option in der ursprünglichen Frage verwiesen wird.

I „chunk“ die Datei in VBA und übertragen jeden „Brocken“ an den Webdienst. Ich Basis der VBA Teil der Lösung auf dem Code finden Sie hier: von Chunk Große Kopieren mit Fortschritt Benachrichtigung . Statt auf das Dateisystem zu kopieren, aber ich sende es an den Server.

Der Code: VBA Land

Hier ist der (fugly) VBA-Code, der die Datei erstellt chunks:

Function CopyFileByChunk(fileName As String, sSource As String) As Boolean

   Dim FileSize As Long, OddSize As Long, SoFar As Long
   Dim Buffer() As Byte, f1 As Integer, ChunkSize As Long

   On Error GoTo CopyFileByChunk_Error

   f1 = FreeFile: Open sSource For Binary Access Read As #f1
   FileSize = LOF(f1)
   If FileSize = 0 Then GoTo Exit_CopyFileByChunk ' -- done!

   ChunkSize = 5505024 '5.25MB
   OddSize = FileSize Mod ChunkSize

   Dim index As Integer
   index = 0

   If OddSize Then
      ReDim Buffer(1 To OddSize)
      Get #f1, , Buffer

      index = index + 1
      SoFar = OddSize

      If UploadFileViaWebService(Buffer, fileName, index, SoFar = FileSize) Then
            g_frmProgress.lblProgress = "Percent uploaded: " & Format(SoFar / FileSize, "0.0%")
            Debug.Print SoFar, Format(SoFar / FileSize, "0.0%")
            DoEvents
         Else
            GoTo CopyFileByChunk_Error
         End If
   End If

   If ChunkSize Then
      ReDim Buffer(1 To ChunkSize)
      Do While SoFar < FileSize
         Get #f1, , Buffer

         index = index + 1
         SoFar = SoFar + ChunkSize

         If UploadFileViaWebService(Buffer, fileName, index, SoFar = FileSize) Then
            g_frmProgress.lblProgress = "Percent uploaded: " & Format(SoFar / FileSize, "0.0%")
            Debug.Print SoFar, Format(SoFar / FileSize, "0.0%")
            DoEvents
         Else
            GoTo CopyFileByChunk_Error
         End If
      Loop
   End If

   CopyFileByChunk = True

Exit_CopyFileByChunk:
   Close #f1
   Exit Function

CopyFileByChunk_Error:
   CopyFileByChunk = False
   Resume Exit_CopyFileByChunk
End Function

Hier ist die referenzierte VBA-Methode, die die Stücke auf den Server hochgeladen:

Public Function UploadFileViaWebService(dataChunk() As Byte, fileName As String, index As Integer, lastChunk As Boolean) As Boolean

    On Error GoTo ErrHand
    Dim blnResult As Boolean
    blnResult = False

        'mdlConvert.SetProgressInfo "Connecting to the web server:" & vbNewLine & _
            DQUOT & server_title() & DQUOT
        If InternetAttemptConnect(0) = 0 Then
            On Error Resume Next

            Dim strSoapAction As String
            Dim strXml As String
            strXml = "<?xml version=""1.0"" encoding=""utf-8""?>" & _
            "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" & _
            "<soap:Body>" & _
            "<UploadZipFile xmlns=""http://something.com/"">" & _
            "<zipBytes></zipBytes>" & _
            "<index>" & index & "</index>" & _
            "<isLastChunk>" & IIf(lastChunk, 1, 0) & "</isLastChunk>" & _
            "</UploadZipFile>" & _
            "</soap:Body>" & _
            "</soap:Envelope>"

            Dim objXmlhttp As Object
            Dim objDom As Object
            Set objXmlhttp = New MSXML2.xmlhttp

            ' Load XML
            Set objDom = CreateObject("MSXML2.DOMDocument")
            objDom.LoadXML strXml

            'insert data chunk into XML doc
            objDom.SelectSingleNode("//zipBytes").dataType = "bin.base64"
            objDom.SelectSingleNode("//zipBytes").nodeTypedValue = dataChunk

            ' Open the webservice
            objXmlhttp.Open "POST", webServiceUrl, False

            ' Create headings
            strSoapAction = "http://something.com/UploadZipFile"
            objXmlhttp.setRequestHeader "Content-Type", "text/xml; charset=utf-8"
            objXmlhttp.setRequestHeader "SOAPAction", strSoapAction

            ' Send XML command
            objXmlhttp.send objDom.XML

            ' Get all response text from webservice
            Dim strRet
            strRet = objXmlhttp.responseText

            ' Close object
            Set objXmlhttp = Nothing
            Set objDom = Nothing

            'get the error if any
            Set objDom = CreateObject("MSXML2.DOMDocument")
            objDom.LoadXML strRet
            Dim isSoapResponse As Boolean
            isSoapResponse = Not (objDom.SelectSingleNode("//soap:Envelope") Is Nothing)
            Dim error As String
            If Not isSoapResponse Then
                error = "Woops"
            Else
                error = objDom.SelectSingleNode("//soap:Envelope/soap:Body/soap:Fault/faultstring").text
            End If
            If error <> "" Then
                ShowServerError error, True
                blnResult = False
            Else
                Err.Clear 'clear the error caused in the XPath query above
                blnResult = True
            End If
            'close dom object
            Set objDom = Nothing


         Else
             GetErrorInfo "UploadFileViaWebService:InternetCheckConnection"
        End If

ErrHand:
    If Err.Number <> 0 Then
        ShowError Err, "UploadFileViaWebService"
        blnResult = False
    End If

    UploadFileViaWebService = blnResult
End Function

Der Code: C # ASMX Web Service

Nun, auf der Server-Seite übernimmt die Web-Service-Methode einige wichtige Parameter.

  1. string filename: Der Name der Datei (jeder Chunk hat die gleiche Datei Name)
  2. byte [] zipBytes: Der Inhalt jedes Chunk
  3. int Index: Der Index (In Verbindung mit dem Dateinamen verwendet, zu bieten einzigartige geordnete Teil Dateien auf dem Dateisystem)
  4. bool isLastChunk: Dies ist die „Ich bin fertig - gehen Sie vor und verschmelzen alle „Brocken“ und Aufräumarbeiten nach sich selbst“Flagge.

int index und Bool isLastChunk. Mit diesem Zusammenhang von der VBA Welt zur Verfügung gestellt, ich weiß genug, um jede dieser Datei Brocken zu speichern und sie dann kombinieren, wenn die isLastChunk Flag wahr ist.

   /// <summary>
    /// Accepts a chunk of a zip file.  Once all chunks have been received,  combines the chunks into a zip file that is processed.
    /// </summary>
    /// <param name="fileName">Name of the file.</param>
    /// <param name="zipBytes">The collection of bytes in this chunk.</param>
    /// <param name="index">The index of this chunk.</param>
    /// <param name="isLastChunk">if set to <c>true</c> this is the last chunk.</param>
    /// <returns>Whether the file was successfully read and parsed</returns>
    /// <exception cref="ParserException">An error occurred while trying to upload your file. The details have been written to the system log.</exception>
    [WebMethod]
    public bool UploadZipFile(string fileName, byte[] zipBytes, int index, bool isLastChunk)
    {
        try
        {
            const string ModuleRootUrl = "/Somewhere/";
            string folderName = HostingEnvironment.MapPath("~" + ModuleRootUrl);
            string fullDirectoryName = Path.Combine(folderName, Path.GetFileNameWithoutExtension(fileName));

            try
            {
                if (!Directory.Exists(fullDirectoryName))
                {
                    Directory.CreateDirectory(fullDirectoryName);
                }

                string pathAndFileName = Path.Combine(fullDirectoryName, AddIndexToFileName(fileName, index));
                using (var stream = new MemoryStream(zipBytes))
                {
                    WriteStreamToFile(stream, pathAndFileName);
                }

                if (isLastChunk)
                {
                    try
                    {
                        MergeFiles(fullDirectoryName, fileName, index);

                        // file transfer is done.
                        // extract the zip file
                        // and do whatever you need to do with its contents
                        // we'll assume that it works - but your "parsing" should return true or false
                        return true;
                    }
                    finally
                    {
                        DeleteDirectoryAndAllContents(fullDirectoryName);
                    }
                }
            }
            catch
            {
                DeleteDirectoryAndAllContents(fullDirectoryName);
                throw;
            }
        }
        return false;
    }

Hier ist der C # -Code, daß jeder neue Chunk die das Dateisystem schreibt:

/// <summary>
/// Writes the contents of the given <paramref name="stream"/> into a file at <paramref name="newFilePath"/>.
/// </summary>
/// <param name="stream">The stream to write to the given file</param>
/// <param name="newFilePath">The full path to the new file which should contain the contents of the <paramref name="stream"/></param>
public static void WriteStreamToFile(Stream stream, string newFilePath)
{
    using (FileStream fs = File.OpenWrite(newFilePath))
    {
        const int BlockSize = 1024;
        var buffer = new byte[BlockSize];
        int numBytes;
        while ((numBytes = stream.Read(buffer, 0, BlockSize)) > 0)
        {
            fs.Write(buffer, 0, numBytes);
        }
    }
}

Hier ist der C # -Code, um alle der Zip-Datei "Chunks" merge:

/// <summary>
/// Merges each file chunk into one complete zip archive.
/// </summary>
/// <param name="directoryPath">The full path to the directory.</param>
/// <param name="fileName">Name of the file.</param>
/// <param name="finalChunkIndex">The index of the last file chunk.</param>
private static void MergeFiles(string directoryPath, string fileName, int finalChunkIndex)
{
    var fullNewFilePath = Path.Combine(directoryPath, fileName);

    using (var newFileStream = File.Create(fullNewFilePath))
    {
        for (int i = 1; i <= finalChunkIndex; i++)
        {
            using (var chunkFileStream = new FileStream(AddIndexToFileName(fullNewFilePath, i), FileMode.Open))
            {
                var buffer = new byte[chunkFileStream.Length];
                chunkFileStream.Read(buffer, 0, (int)chunkFileStream.Length);
                newFileStream.Write(buffer, 0, (int)chunkFileStream.Length);
            }
        }
    }
}

Andere Tipps

Ich habe große Dateien wie diese mit MTOM-Codierung übertragen.

Weitere Informationen über MTOM hier: http://msdn.microsoft.com /en-us/library/aa395209.aspx

Sie können eine MTOM Probe hier herunterladen: http://msdn.microsoft .com / en-us / library / ms751514.aspx

Überprüfen Sie heraus Bustamante Buch über WCF, wenn Sie mehr auf MTOM wollen.

Wie für den Anruf VBA, ich bin kein Experte auf diesem Gebiet also ich habe keine Informationen in Bezug auf sie haben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top