Wcf 如何将大文件从MS Word加载项(VBA)传输到Web服务器? 概述

Wcf 如何将大文件从MS Word加载项(VBA)传输到Web服务器? 概述,wcf,vba,asmx,large-files,data-transfer,Wcf,Vba,Asmx,Large Files,Data Transfer,我有一个用VBA(Visual Basic for Applications)编写的Microsoft Word加载项,它将文档及其所有相关内容(嵌入式媒体)压缩到zip存档中。创建zip存档后,它将文件转换为字节数组,并将其发布到ASMXWeb服务。这主要是有效的 问题 我遇到的主要问题是将大型文件传输到网站。我可以成功上传一个40MB左右的文件,但不是140MB(超时/一般故障) 第二个问题是,如果zip归档文件太大,则在VBScript Word加载项中构建字节数组可能会因客户端计算机内存

我有一个用VBA(Visual Basic for Applications)编写的Microsoft Word加载项,它将文档及其所有相关内容(嵌入式媒体)压缩到zip存档中。创建zip存档后,它将文件转换为字节数组,并将其发布到ASMXWeb服务。这主要是有效的

问题 我遇到的主要问题是将大型文件传输到网站。我可以成功上传一个40MB左右的文件,但不是140MB(超时/一般故障)

第二个问题是,如果zip归档文件太大,则在VBScript Word加载项中构建字节数组可能会因客户端计算机内存不足而失败

潜在解决方案 我正在考虑以下选项,并期待任何一个选项或任何其他建议的反馈

选择一 在客户端(MS Word VBA)上打开一个文件流,一次读取一个“块”,并传输到ASMX web服务,后者将“块”组装到服务器上的文件中

这样做的好处是不会向应用程序添加任何其他依赖项或组件,我只会修改现有的功能。(依赖关系越少越好,因为此解决方案应适用于各种服务器环境,并且相对容易设置。)

问题:
  • 是否有这样做的例子或任何推荐的技术(在VBA中的客户机上或在C#/VB.NET中的web服务中)
选择二 我知道WCF可以通过“分块”或流式传输数据来解决传输大文件的问题。然而,我对WCF不是很熟悉,也不确定它到底能做什么,或者我是否能从VBA与WCF服务进行通信。这有添加另一个依赖项(.NET 3.0)的缺点。但是如果使用WCF确实是一个更好的解决方案,我可能不介意使用这种依赖性

问题:
  • WCF是否可靠地支持这种性质的大型文件传输?如果是,这涉及到什么?有什么资源或例子吗
  • 您能从VBA调用WCF服务吗?有什么例子吗

我使用MTOM编码传输了这样的大文件

有关MTOM的更多信息,请点击此处:

您可以在此处下载MTOM示例:

如果您想了解更多关于MTOM的信息,请查看Bustamante关于WCF的书


至于VBA电话,我不是该领域的专家,因此我没有关于它的任何信息

我最终实现了原始问题中提到的选项一

我在VBA中“分块”文件,并将每个“分块”传输到web服务。我将解决方案的VBA部分基于以下代码:。但是,我没有复制到文件系统,而是将其发送到服务器

代码:VBA Land 下面是创建文件块的(fugly)VBA代码:

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
/// <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);
            }
        }
    }
}
函数CopyFileByChunk(文件名为字符串,sSource为字符串)为布尔值
Dim FileSize为Long,OddSize为Long,SoFar为Long
Dim Buffer()作为字节,f1作为整数,ChunkSize作为长度
On错误转到CopyFileByChunk\u错误
f1=FreeFile:打开源进行二进制访问,读取为#f1
FileSize=LOF(f1)
如果FileSize=0,则转到退出\u CopyFileByChunk'--完成!
ChunkSize=5505024'5.25MB
OddSize=文件大小Mod ChunkSize
将索引设置为整数
索引=0
如果大小不一样的话
ReDim缓冲区(1到OddSize)
获取#f1,缓冲区
索引=索引+1
SoFar=奇数
如果通过WebService上传文件(缓冲区、文件名、索引、SoFar=FileSize),则
g_frmProgress.lblProgress=“上传百分比:”&格式(SoFar/FileSize,“0.0%”)
调试。打印SoFar,格式(SoFar/FileSize,“0.0%”)
多芬特
其他的
转到CopyFileByChunk\u错误
如果结束
如果结束
如果是大的话
ReDim缓冲区(1到ChunkSize)
在当前<文件大小时执行
获取#f1,缓冲区
索引=索引+1
SoFar=SoFar+ChunkSize
如果通过WebService上传文件(缓冲区、文件名、索引、SoFar=FileSize),则
g_frmProgress.lblProgress=“上传百分比:”&格式(SoFar/FileSize,“0.0%”)
调试。打印SoFar,格式(SoFar/FileSize,“0.0%”)
多芬特
其他的
转到CopyFileByChunk\u错误
如果结束
环
如果结束
CopyFileByChunk=True
退出\u CopyFileByChunk:
关闭#f1
退出功能
CopyFileByChunk\u错误:
CopyFileByChunk=False
恢复退出\u CopyFileByChunk
端函数
以下是将块上载到服务器的引用VBA方法:

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
Public函数UploadFileViaWebService(dataChunk()作为字节,fileName作为字符串,index作为整数,lastChunk作为布尔值)作为布尔值
在错误上走错
Dim blnResult为布尔值
blnResult=False
“mdlConvert.SetProgressInfo”连接到web服务器:”&vbNewLine&_
DQUOT&server_title()&DQUOT
如果InternetAttemptConnect(0)=0,则
出错时继续下一步
作为字符串的Dim strSoapAction
将strXml设置为字符串
strXml=”“&_
"" & _
"" & _
"" & _
"" & _
索引(&index&)_
&IIf(lastChunk,1,0)&&_
"" & _
"" & _
""
Dim objXmlhttp作为对象
作为对象的Dim objDom
设置objXmlhttp=New MSXML2.xmlhttp
'加载XML
设置objDom=CreateObject(“MSXML2.DOMDocument”)
objDom.LoadXML strXml
'将数据块插入XML文档
objDom.SelectSingleNode(//zipBytes”).dataType=“bin.base64”
objDom.SelectSingleNode(“//zipBytes”).nodeTypedValue=dataChunk
'打开Web服务
objXmlhttp.Open“POST”,webServiceUrl,False
'创建标题
strSoapAction=”http://something.com/UploadZipFile"
objXmlhttp.setRequestHeader
/// <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);
        }
    }
}
/// <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);
            }
        }
    }
}