从字节数组或流中获取文件名

32

从字节数组或流中获取文件名是否可能?我只想检索文件名,而不是保存文件。

6个回答

55
如果Stream实际上是一个FileStream,那么可以通过将其转换为FileStream类型并访问.Name属性来获取此信息。
Stream stream = ...
FileStream fs = stream as FileStream;
if(fs != null) Console.WriteLine(fs.Name);

然而,在一般情况下,不是所有的流都提供文件名,包括byte[]和大多数其他类型的流。同样地,即使底层流(在几个层次下)是一个文件,被其他流(如压缩、加密、缓冲等)包装的FileStream基础流也不会显示这些信息。

我建议将文件名单独处理。


+1 以前从没注意过这个属性,虽然我可能永远也不会用它哈哈 - Adam Houldsworth
嗨@Marc,谢谢您的出色解释。我会尝试并发布结果。 - Gus
嗨,在WCF中我返回了一个FileStream,但客户端得到的是MessageBodyStream。有什么建议吗? - Gus
@Gus是的:那么您就无法访问文件名 - 只有WCF上传的流内容。 - Marc Gravell
1
如果我一开始就把这篇文章读到底,那么我可以省下好几个小时。感谢这篇解释。 - Misters

15

不,这是不可能的(好的,也许在 FileStream 类上可能是可能的,每天都能学到新东西!)。

一个字节数组或流代表着文件的内容,而不是Windows关于文件的元数据

有很多简单的方法可以保留这些信息,但不知道更多关于您情况的信息,我无法提供解决方案。


4

所有文件信息(如名称、扩展名等)都是实际文件的元数据。字节数组仅包含实际数据。如果字节数组本身包含元数据(例如一个XML文件),则可能是可行的...但是,您需要知道类型和具体查找位置。


0

你无法从字节数组中获取文件名。相反,你需要使用文件流来获取文件名。字节数组不存储名称。


0
可能已经有点晚了,但这是我的解决方案。 这适用于 PE 文件,例如 .exe 和 .dll。 该代码逐字节读取,直到找到 OriginalFilename 属性(如果您在记事本中打开 .exe,也可以看到它)。必须设置 .exe 的 OriginalFilename 属性才能正常工作(否则返回 null)。
public string GetOriginalFilenameFromStream(Stream stream)
{
    if (stream == null)
        return null;

    string lookupString = "O\0r\0i\0g\0i\0n\0a\0l\0F\0i\0l\0e\0n\0a\0m\0e"; // OriginalFilename with null char between each letter
    int originalFileNameLength = 550; // I bit more than 512 (256 * 2 (for the nulls)) + the possible extensions

    //Go at the end of the stream
    stream.Seek(0, SeekOrigin.Begin);
    long currentPosition = 0;
    bool hasFoundBytes = false;

    byte[] bit = new byte[1] { 0 };

    char[] lookupStringChars = lookupString.ToCharArray();
    byte[] lookupBytes = Encoding.ASCII.GetBytes(lookupStringChars);

    do
    {
        for (int i = 0; i < lookupBytes.Length; i++)
        {
            if (bit[0] == lookupBytes[i])
            {
                hasFoundBytes = true;
                if (i == lookupBytes.Length - 1)
                    break; // Stops reading if the end of the lookupBytes has been reached (string has been found)
                else
                    currentPosition = stream.Read(bit, 0, 1);
            }
            else
            {
                hasFoundBytes = false;
                currentPosition = stream.Read(bit, 0, 1);
                break;
            }
        }
    } while (currentPosition != 0 && !hasFoundBytes && stream.Position < 1073741824 /* 1 MB */);

    if (!hasFoundBytes)
    {
        // Lookup not found in the file
        return null;
    }

    // Gets the OriginalFilename from the stream
    byte[] originalFilenameByteArray = new byte[originalFileNameLength];
    stream.Read(originalFilenameByteArray, 0, originalFileNameLength);

    string parsedOriginalFilename = ParseOriginalFilename(Encoding.ASCII.GetString(originalFilenameByteArray));
    _logWriter?.Info($"{this.GetType().Name} {nameof(GetOriginalFilenameFromStream)} returns {parsedOriginalFilename}");

    return parsedOriginalFilename;

    string ParseOriginalFilename(string stringToParse)
    {
        // Gets the text between the 2 first 3 nulls \0\0\0
        string nullChar = "\0";
        string threeNulls = string.Format("{0}{0}{0}", nullChar);
        int pFrom = stringToParse.IndexOf(threeNulls) + threeNulls.Length;
        int pTo = stringToParse.IndexOf(threeNulls, pFrom);
        string originalFilename = stringToParse.Substring(pFrom, pTo - pFrom);

        // Removes the nulls between each letters
        originalFilename = originalFilename.Replace(nullChar, "");
        return originalFilename;
    }
}

二进制文件看起来像这样(\0是空字符): O\0r\0i\0g\0i\0n\0a\0l\0F\0i\0l\0e\0n\0a\0m\0e\0\0\0T\0e\0s\0t\0A\0p\0p\0.\0e\0x\0e\0\0\0... 解析后的原始文件名如下: TestApp.exe 希望这可以帮到您。

0
如果字节数组包含文件版本信息,如原始文件名、内部名称、文件版本和产品名称,您可以获取它们。但是,如果您想从文件字节数组中获取文件的完整路径,这是不可能的,因为完整路径与磁盘路径相关,而不是文件数据。
根据@Steve Rousseau的回答,我将代码转换为我熟悉的PowerShell,并进行了一些小的修改。
function Get-AppInfoFromBytes{
    [CmdletBinding()]
    param(
        $bytes,
        [string]
        $VersionInfo="OriginalFilename"
    )
    
    if ( $bytes -IsNot [byte[]] ) {throw 'byte data required';}
    if ( $bytes.Length -gt 80kb -Or $bytes.Length -lt 3kb ) {write-warning 'A BYTE LENGTH OF 80KB MAX OR 3KB MIN FROM HEAD OR TAIL OF A FILE IS ENOUGH TO CONTAIN THE INFO';}
        
    # preserving the nullchar effect in string 
    [string]$lookupString = iex -command "echo $( ($VersionInfo -replace '(.)','`0$1').remove(0,2) )"
    [int16]$AppInfoValueLength = 550; # // I bit more than 512 (256 * 2 (for the nulls)) + the possible extensions
    
    write-verbose "searching property `"$lookupString`""
    [int32]$position=0
    
    $position=([System.Text.Encoding]::ASCII.GetString($bytes)).Indexof($lookupString)
    if ($Position -eq -1) {
        write-verbose "VersionInfo `"$VersionInfo`" property was not Found`nplease check other properties or supply another bytes portion"
        return $null;
    }

    write-verbose "Gets the Info from byte data...note that even the info exist it may contain an empty string"
    [byte[]]$bytes=$bytes[$position..($position+$AppInfoValueLength)]
    [string]$S=[System.Text.Encoding]::ASCII.GetString($bytes);
    $3Nuls =  "`0`0`0"
    $3NulsStartIdx1 = $S.IndexOf($3Nuls) + 3
    $3NulsStartIdx2 = $S.IndexOf($3Nuls, $3NulsStartIdx1);
    return $S.Substring($3NulsStartIdx1,$3NulsStartIdx2-$3NulsStartIdx1).Replace("`0", "" )
}

你可以这样调用函数:
#getting the bytes methods: 
PS > [byte[]]$bytes=cat "C:\Users\Zdilto\Desktop\File.exe" -tail 3kb -enc byte
PS > $bytes=cat "C:\Users\Zdilto\Desktop\File.exe" -total 76kb -AsByteStream
PS > $bytes=[io.file]::ReadAllBytes("C:\Users\Zdilto\Desktop\File.exe")
PS > $bytes=$bytes[0..10kb]


#calling the function:
PS > Get-AppInfoFromBytes -b $bytes -verbose;
PS > Get-AppInfoFromBytes -b $bytes -v "LegalCopyright" -verbose;
PS > Get-AppInfoFromBytes -b $bytes -v "InternalName";
PS > Get-AppInfoFromBytes -b $bytes -v "ProductName";
PS > Get-AppInfoFromBytes -b $bytes -v "Comments"  -verbose;

#testing result:
PS > $result=Get-AppInfoFromBytes -byte $bytes -VersionInfo Comments -verbose;
PS > if( [string]::IsNullOrEmpty($result) ) {"got no value"} else {"success"}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接