打开从sql server下载的Office文件时出错

时间:2012-10-02 14:35:17

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

原始office文件没有问题(我试过word和excel),但是当我将文件作为二进制文件上传到数据库然后从那里下载到我的PC并打开下载的文件时,我收到警告消息“excel发现内容无法读取”等文件与原始文件相比变得越来越简洁。

确切的错误消息是; “Excel在filename.xls中找到了不可读的内容。是否要恢复此工作簿的内容?如果您信任此工作簿的来源,请单击是。”

我如何上传文件:

'UPLOAD FILE
Dim fi As New FileInfo(FilePath)
Dim s As Stream = fi.OpenRead()
Dim buffer(fi.Length) As Byte 'I put the buffer in database in varbinary(max) format
s.Read(buffer, 0, fi.Length)
s.Close()

我如何下载并打开文件:

'DOWNLOAD FILE
Dim fi As New FileInfo(FilePath)
Dim s As Stream = fi.OpenWrite()
Dim buffer As Byte() = reader("Binary")
s.Write(buffer, 0, buffer.Length)
s.Close()

'OPEN FILE
Dim p As New Process
p.StartInfo = New ProcessStartInfo(FilePath)
p.Start()

更新

正如所建议的那样,我尝试简单地复制文件,将SQL完全排除在外。这是我尝试的代码,也失败了:

Private Sub CopyFile(ByVal filePath As String)
    Dim fi As New FileInfo(filePath)
    Dim s As Stream = fi.OpenRead()
    Dim buffer(CType(fi.Length, Integer) - 1) As Byte
    s.Read(buffer, 0, CType(fi.Length, Integer))
    s.Close()

    Dim fi2 As New FileInfo(filePath & " Copy")
    Dim s2 As Stream = fi2.OpenWrite()
    s2.Write(buffer, 0, buffer.Length)
    s2.Close()
End Sub

1 个答案:

答案 0 :(得分:3)

this MSDN article所述,VB.NET数组的声明方式与其他语言不同,例如在C#中。在其他语言中,当声明固定长度数组时,给定的大小用作数组的总长度。例如,在c#中,语句fixed byte buffer[3];将声明一个包含3个元素(索引0到2)的字节数组。但是,在VB中,声明固定数组时给出的大小用作数组的上限,而不是大小。因此,在VB中,语句Dim buffer(3) As Byte声明一个包含4个元素的字节数组(索引为0到3)。

考虑到这一点,如果仔细观察代码,实际上是在声明一个字节数组,它是一个大于文件大小的元素(索引为0到fi.Length)。然后,您将整个文件读入字节数组,从索引0开始。因此,数组中的最后一个字节保留为值0(空字符)。然后,您将字节数组的全部内容写出到一个新文件,包括最后一个空字节。因此,您的代码正确地复制了文件中的所有字节,但是在它的末尾添加了一个额外的空字节,这使得Excel不满意。如果查看原始文件大小和新创建文件的文件大小,您将看到新文件比原始文件大一个字节。

要解决此问题,您只需在声明数组时调整数组的大小,使其与文件的长度完全相同:

Dim buffer(fi.Length - 1) As Byte

然而,虽然我很喜欢,但我觉得有必要在你发布的代码中指出其他一些改进方面。首先,当使用实现IDisposable接口的对象(例如Stream)时,最好尽可能使用Using语句。这样做可确保即使遇到异常,也始终正确关闭/处置对象。例如,如果你这样做会更好:

Dim fi As New FileInfo(FilePath)
Using s As Stream = fi.OpenRead()
    Dim buffer(fi.Length - 1) As Byte
    s.Read(buffer, 0, fi.Length)
End Using

此外,很明显你没有使用Option Strict,因为如果你是,你将不被允许使用fi.Length(Long)作为数组大小的参数或者缓冲长度。如果您启用Option Strict,则系统会强制您明确声明要将Long值转换为Integer。例如:

Dim fi As New FileInfo(FilePath)
Using s As Stream = fi.OpenRead()
    Dim buffer(CInt(fi.Length) - 1) As Byte
    s.Read(buffer, 0, CInt(fi.Length))
End Using

在大多数情况下启用Option Strict是一个非常好的主意。它会强制您了解变量类型以及何时可能丢失数据。例如,在这种情况下,通过启用Option Strict,您就会意识到文件大小(Long.MaxValue)可能大于数组的最大长度(Integer.MaxValue ),所以,如果你想处理非常大的文件,你需要编写一个循环来读取和写入文件。即使你不想处理大文件,最好先检查大小,这样你就可以优雅地处理错误,而不是抛出溢出异常:

If fi.Length >= Integer.Max Then
    'Display or log error that the file is too large, and skip loading the file
Else
    'Load file
End If

最后,如果您不关心处理大文件,可以使用更简单的方法来读取和写入文件中的所有字节:

'UPLOAD FILE
Dim buffer() As Byte = File.ReadAllBytes(filePath)

'DOWNLOAD FILE
Dim buffer As Byte() = reader("Binary")
File.WriteAllBytes(filePath, buffer)