Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read files on Android phone from C# program on Windows 7?

When I connect my Android phone to my Windows 7 with USB cable, Windows pop-up a window and show me the phone's internal storage at Computer\HTC VLE_U\Internal storage from Windows Explorer. But there is no drive letter linked with this phone storage! Inside Windows Explorer, I can manipulate the file system.

How can I manipulate the same files or folders from C# program?

As I tested,

DirectoryInfo di = new DirectoryInfo(@"C:\"); 

works, but

DirectoryInfo di = new DirectoryInfo(@"Computer\HTC VLE_U\Internal storage");

failed.

But in Windows Explorer, IT IS Computer\HTC VLE_U\Internal storage! No drive letter!

Yes, this is MTP device.

I see this answer in Stack Overflow, but the return results are empty for me after running this code

var drives = DriveInfo.GetDrives();
var removableFatDrives = drives.Where(
    c=>c.DriveType == DriveType.Removable &&
    c.DriveFormat == "FAT" && 
    c.IsReady);
var androids = from c in removableFatDrives
    from d in c.RootDirectory.EnumerateDirectories()
    where d.Name.Contains("android")
    select c;

I get correct drives. But android phone's internal storage is not here. Both removableFatDrives and androids are empty for me.

like image 449
Herbert Yu Avatar asked Jul 30 '14 00:07

Herbert Yu


2 Answers

I used nugetpackage "Media Devices by Ralf Beckers v1.8.0" This made it easy for me to copy my photos from my device to my computer and vice versa.

 public class Program
{
    static void Main(string[] args)
    {
        var devices = MediaDevice.GetDevices();
        using (var device = devices.First(d => d.FriendlyName == "Galaxy Note8"))
        {
            device.Connect();
            var photoDir = device.GetDirectoryInfo(@"\Phone\DCIM\Camera");

            var files = photoDir.EnumerateFiles("*.*", SearchOption.AllDirectories);

            foreach (var file in files)
            {
                MemoryStream memoryStream = new System.IO.MemoryStream();
                device.DownloadFile(file.FullName, memoryStream);
                memoryStream.Position = 0;
                WriteSreamToDisk($@"D:\PHOTOS\{file.Name}", memoryStream);
            }
            device.Disconnect();
        }

    }

    static void WriteSreamToDisk(string filePath, MemoryStream memoryStream)
    {
        using (FileStream file = new FileStream(filePath, FileMode.Create, System.IO.FileAccess.Write))
        {
            byte[] bytes = new byte[memoryStream.Length];
            memoryStream.Read(bytes, 0, (int)memoryStream.Length);
            file.Write(bytes, 0, bytes.Length);
            memoryStream.Close();
        }
    }
}
like image 96
ZackOfAllTrades Avatar answered Oct 05 '22 23:10

ZackOfAllTrades


This question definitely help me! I was trying to write a simple program to auto sync my photos/videos from an android device to my computer. Hence I ended up asking the same question as Herbert Yu, how do I access the device path when there are no drive letters?

ZackOfAllTrades' answer helped me. Thumbs up!

I copied ZackOfAllTrades' code, tweaked it to my needs and Bang! it worked, but not for long. I have a 108MP camera on my Xiaomi Note 10 Pro, 4 minutes videos can easily go past 1GB; some of my videos are 4GBs large. using ZackOfAllTrades' code, I quickly ran into OutOfMemoryException when invoking MediaDevice.DownloadFile(string, Stream).

[1] My initial attempt at fixing this is to go to project properties, set my project to build for x64, that seems to get rid of OutOfMemoryException; I am able to start copying files between 1GB to 2GB without any problems.

[2] However, as soon as I start copying files of 2.5GB large, the WriteStreamToDisk() util function written by ZackOfAllTrades started to complain about Stream too long.

Then I realized that DownloadFile takes a Stream object, it doesn't need to be a MemoryStream as Zack has used. So I switched it to a FileStream object as follow

static void Main(string[] args)
{
    string DeviceNameAsSeenInMyComputer = "Mi Note 10 Pro";

    var devices = MediaDevice.GetDevices();

    using (var device = devices.Where(d => d.FriendlyName == DeviceNameAsSeenInMyComputer || d.Description == DeviceNameAsSeenInMyComputer).First())
    {
        device.Connect();
        var photoDir = device.GetDirectoryInfo(@"\Internal shared storage\DCIM\Camera");
        var files = photoDir.EnumerateFiles("*.*", SearchOption.TopDirectoryOnly);

        foreach (var file in files)
        {
            string destinationFileName = $@"F:\Photo\{file.Name}";
            if (!File.Exists(destinationFileName))
            {
                using (FileStream fs = new FileStream(destinationFileName, FileMode.Create, System.IO.FileAccess.Write))
                {
                    device.DownloadFile(file.FullName, fs);
                }
            }

            
        }
        device.Disconnect();
    }

    Console.WriteLine("Done...");
    Console.ReadLine();
}

Worked beautifully. When I am using ZackOfAllTrades' code, VS profiler shows memory consumption at about 2.5 times the size of file. Eg: if the file is 1.5GB large, the memory consumption is roughly 4GB.

But if one is to copy to file system directly, the memory consumption is negligible (<50mb)

The other issues is with MediaDevice.FriendlyName. In ZackOfAllTrades' example, he is using a Samsung, and I am guessing Samsung phones supported this property. I did not bother digging out my old Samsung to try this out. I know for sure my Xiaomi Note 10 Pro did not support FriendlyName, instead what worked for me is MediaDevice.Description

Hope this helps others who are asking the same question.

like image 22
Ji_in_coding Avatar answered Oct 05 '22 23:10

Ji_in_coding