【问题标题】:.docx stored in SQL Server results in "there was an error opening the file"存储在 SQL Server 中的 .docx 导致“打开文件时出错”
【发布时间】:2013-11-11 01:02:03
【问题描述】:

我正在使用带有 SQL Server 2005 数据库的 VB.net 4.0 编写的旧版 Windows 桌面应用程序。 该应用程序基于存储在数据库中的 Word 模板生成报告。 有一个管理部分允许将新的 Word 报告模板 (.docx) 加载到数据库中,以防报告中的文本发生一些更改。 管理表单允许添加新文件,或者删除或更新现有文件。当添加一个新文件时,它会要求一个代码(文本)用作存储它的数据库查找表中文件的主键。它还允许从数据库中选择和打开一个文件,以便它可以已查看。

可以一致地重现以下行为: 可以使用新代码(主键)添加新的 .docx 文件。 该文件可以成功打开。 如果使用新的 .docx 更新任何现有文件,则任何打开尝试都将失败,并在 Word 中显示错误消息“打开文件时出错”。 如果删除了该文件的记录,并使用新代码 (PK) 添加了相同的文件,则它可以成功打开。 如果使用与已删除文件相同的代码 (PK) 添加相同的文件(或任何 .docx 文件),它似乎可以写入数据库,但任何打开文件的尝试都将失败,并显示与上述相同的错误消息.

无法打开的 .docx 文件比可以成功打开时长一个字节。 Word 无法打开的.docx 文件仍然可以作为存档打开(以 7zip 为例),文件内容看起来是一样的。

覆盖现有文件,或将已删除文件的代码 (PK) 重新用于任何其他文件类型(包括 .doc)都可以正常工作。只有 .docx 文件会导致问题;遗憾的是,需要 .docx 文件,因为报告生成是通过使用 OpenXML 代码操作 Word 文档内容来实现的。

以下是将文件插入数据库的代码:

Private Sub btnInsert_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles     btnInsert.Click
    Dim sName As String = InputBox("What is the name of this Resource")
    If (sName.Length > 0) Then
        Dim ofd As New OpenFileDialog
        With ofd
            .CheckFileExists = True
            .ShowReadOnly = False
            .Filter = "All Files|*.*"
            If (.ShowDialog = DialogResult.OK) Then
                Dim fs As FileStream = New FileStream(.FileName, FileMode.Open, FileAccess.Read)
                Dim docByte As Byte() = New Byte(fs.Length - 1) {}
                fs.Read(docByte, 0, Convert.ToInt32(fs.Length))
                fs.Close()

                Dim FileType As String = Path.GetExtension(.FileName).ToLower

                Dim conn As New SqlConnection(strConnString)
                Dim cmd As New SqlCommand(sqlInsert, conn)
                cmd.Parameters.AddWithValue("@ID", sName)
                cmd.Parameters.AddWithValue("@Bytes", docByte)
                cmd.Parameters.AddWithValue("@Types", FileType)

                conn.Open()
                cmd.ExecuteNonQuery()
                conn.Close()

                MsgBox("Saved to DB")
                Dim li As ListViewItem = lvwResource.Items.Add(sName)
                li.SubItems.Add(FileType)

                docByte = Nothing
                fs.Dispose()
            End If
        End With
    End If
End Sub

以及检索的代码:

Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpen.Click

    Dim sName As String = lvwResource.SelectedItems(0).Text
    Dim sType As String = lvwResource.SelectedItems(0).SubItems(1).Text
    Dim fileName As String = String.Format("{0}{1}{2}", Path.GetTempPath, sName, sType)

    Dim conn As New SqlConnection(strConnString)
    Dim cmd As New SqlCommand(sqlSelect, conn)
    cmd.Parameters.AddWithValue("@ID", sName)

    Dim da As New SqlDataAdapter(cmd)

    Dim dt As New DataTable
    da.Fill(dt)

    Dim docByte() As Byte = dt.Rows(0)(1)
    Dim fs As New FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)
    fs.Write(docByte, 0, Convert.ToInt32(docByte.Length))
    fs.Dispose()

    Try
        System.Diagnostics.Process.Start(fileName)
    Catch ex As Exception
        MsgBox(ex.Message, MsgBoxStyle.Question, "Unknown Resource Type")
    End Try
End Sub

一些进一步的细节:工作站是带有 Microsoft Office 2003 SP3 的 Windows XP Sp3,服务器是 Server 2003 Standard Edition SP2,db 是 SQL Server 2005 SP2。 这是一个大型组织,我对他们过时的软件没有任何影响。

【问题讨论】:

  • (1) 请发布您用于存储/检索文档对象的代码。 (2) 当您将 .docx 提取到磁盘文件而不是直接在 Word 中打开它时会发生什么?例如。它仍然是 .docx 吗?它与您保存的文件的长度相同吗?更短?更长?所有提取的 .docx 文件的长度是否相同?你还能解压缩它们吗?如果可以,它们包含什么?
  • bibadia - 我已更新帖子以包含您要求的额外详细信息。回答您的问题 (2) 检索代码将文件写入临时目录并从那里打开它。根据文件 Size 属性,它在目录中显示为 .docx 文件,但比原始文件长一个字节(Size on disk 两次都相同) .检索到的文件可以用7zip打开,所有内容都正常。
  • 如果您 (1) 明确地 fs.Close() 和 (2) 为 .docx 文件类型,少写一个字节怎么办?
  • (1) 显式添加 fs.Close() 没有任何区别; (2) 为 .docx 文件类型少写一个字节并没有什么不同。 :(
  • 感谢您的宝贵时间,bibadia。已找到解决方案。

标签: sql-server vb.net ms-word


【解决方案1】:

好的,已找到解决方案。 非常感谢 MSDN 论坛上的 Franklin Chan 付出的时间和耐心帮助我。

错误出现在更新代码中,我从未在此处发布过(我的错!)。

读取文件以更新和现有记录时,我有以下代码:

Dim fs As FileStream = New FileStream(.FileName, FileMode.Open)
Dim img As Byte() = New Byte(fs.Length) {}
fs.Read(img, 0, fs.Length)
fs.Close()

有趣的是,我在插入子中以正确的方式进行操作。正确的语法是:

Dim fs As FileStream = New FileStream(.FileName, FileMode.Open, FileAccess.Read)
Dim img As Byte() = New Byte(fs.Length - 1) {}
fs.Read(img, 0, Convert.ToInt32(fs.Length))
fs.Close()

另一个缺少的重要步骤是在从数据库中提取并打开之前清除临时文件,因此添加:

If System.IO.File.Exists(fileName) = True Then
    System.IO.File.Delete(fileName)
End If

到 btnOpen() 代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 2023-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多