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

发布于 2024-12-24 18:22:10 字数 45 浏览 0 评论 0原文

是否可以从字节数组或流中获取文件名? 我不会保存文件。我只是想找回它的名字。

Is it possible to get filename from the byte array or stream?
I wont to save the file. I just want to retrieve its name.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

懷念過去 2024-12-31 18:22:10

如果 Stream 实际上是 FileStream,则可以通过转换为 FileStream 并访问 FileStream 来使用此 >.Name 属性:

Stream stream = ...
FileStream fs = stream as FileStream;
if(fs != null) Console.WriteLine(fs.Name);

但是,在一般情况下:不,这是不可用的。 byte[]当然没有文件名的概念,大多数其他类型的流也没有。同样,被其他流(压缩、加密、缓冲等)包装的 FileStream 基本流不会公开此类信息,尽管底层流(向下几层)是一个文件。

我会单独处理文件名。

If the Stream is actually a FileStream, then this may be available by casting to FileStream and accessing the .Name property:

Stream stream = ...
FileStream fs = stream as FileStream;
if(fs != null) Console.WriteLine(fs.Name);

However, in the general case: no, this is not available. A byte[] certainly has no concept of a filename, nor do most other types of streams. Likewise, a FileStream base-stream that is being wrapped by other streams (compression, encryption, buffering, etc) will not expose such information, despite the underlying stream (several layers down) being a file.

I would handle the filename separately.

小嗷兮 2024-12-31 18:22:10

不,这是不可能的(好吧,所以在 FileStream 类上可能是可能的,每天学习新东西!)。

字节数组或流表示文件的内容,而不是有关文件的Windows元数据

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

No this isn't possible (ok so it might be possible on the FileStream class, learn something new everyday!).

A byte array or stream represents the content of the file, not the Windows metadata about the file.

There are plenty of straightfoward ways to retain this information, but not knowing any more about your situation I can't offer a solution.

太傻旳人生 2024-12-31 18:22:10

所有文件信息(例如名称、扩展名等)都是实际文件的元数据的一部分。字节数组仅保存实际数据。如果字节数组本身保存元数据(例如 xml 文件),则可能是这样……但是,您需要知道类型以及具体在哪里查找。

All file information (such as name, extension etc.) is part of meta data for an actual file. The byte array will only hold the actual data. It may be possible if the byte array itself holds meta data (example an xml file)... however, you'd need to know the type and specifically where to look.

八巷 2024-12-31 18:22:10

您将无法从字节数组获取文件名。相反,您需要 filestream 来获取文件名。字节数组不存储名称。

You will not able to get filename from byte array. Instead you need filestream to get the name of the file. Byte array does not store name.

画中仙 2024-12-31 18:22:10

可能已经晚了,但这是我的解决方案。
这适用于 .exe 和 .dll 等 PE 文件。
该代码逐字节读取,直到找到 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

希望这有帮助

It might be late for this but here's my solution.
This works for PE file such as .exe and .dll.
The code reads byte by byte until it finds the OriginalFilename property (it can also be seen if you open the .exe in Notepad. OriginalFilename property of the .exe needs to be set in order to work properly (or it returns 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(
quot;{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;
    }
}

The binary looks like this (\0 is a null characther):
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...
And the parsed Original Filename looks like this:
TestApp.exe

Hope this helps

你与昨日 2024-12-31 18:22:10

如果字节数组包含文件版本信息,例如originalFilname,InternalName,FileVersion,ProductName,您可以获取它
但如果您想从文件字节数组中获取文件的完整路径,这是不可能的,因为完整路径与磁盘路径相关,而不与文件数据相关。

基于@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"}

if the bytes array contains file version informations like originalFilname,InternalName,FileVersion,ProductName you can get it
but if you want to get fullpath of a file from file byte array it would impossible since fullpath is related to disk path not to file data.

based on @Steve Rousseau answer i converted the code to work in powershell that i'm familiar with, and add minor modifications

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", "" )
}

you can call the function like that:

#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"}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文