Get file name from byte array or Stream
Asked Answered
S

6

33

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.

Steed answered 5/1, 2012 at 11:46 Comment(0)
C
57

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.

Comptroller answered 5/1, 2012 at 11:50 Comment(5)
+1 Never spotted that property before, though I'm not likely to ever use it lolFogy
Hi @Marc, thanks for your excellent explanation. I'll try and post the result.Steed
Hi, In wcf I return a FileStream, but the client I get a MessageBodyStream. Any suggestions?Steed
@Steed yes: then you don't have access to the filename - only the stream contents are uploaded by WCFComptroller
I would it have saved hours if i just would it read this post till the end. Thanks for this explination.Siegbahn
F
15

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.

Fogy answered 5/1, 2012 at 11:47 Comment(0)
P
4

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.

Persas answered 5/1, 2012 at 11:48 Comment(0)
S
0

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.

Selfimprovement answered 16/9, 2018 at 10:13 Comment(0)
P
0

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($"{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

Preheat answered 4/5, 2023 at 18:35 Comment(0)
A
0

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"}
Administrate answered 19/9, 2023 at 12:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.