C# 为什么MtpDevice.ImportFile总是失败?
我正在修改一个现有的应用程序,它是用C#in Xamarin编写的,专门为Android构建的 现有的应用程序在摄像头的wifi卡上搜索文件,并通过HTTP接口下载。我试图让它下载的文件使用拴在一个USB电缆代替,但无法得到文件下载 我正在使用MtpDevice尝试下载,但ImportFile函数总是失败。不幸的是,它从未抛出异常,也没有给出任何关于原因的有用信息 下面的代码显示了我正在做的事情,尽管我已经删除了一些与问题无关的东西 当我修改现有的应用程序时,代码有点复杂,因为有一种方法可以找到文件,然后后台线程调用另一种方法来进行实际下载,因此用户可以在下载文件时继续工作 另外,我在GetFileListAsync中显示的一些代码实际上也存在于其他方法中,因为它们被多次使用。。。我没有包括权限的东西,因为这一切似乎都很好 这是首先被调用的方法,用于查找对象句柄列表C# 为什么MtpDevice.ImportFile总是失败?,c#,android,xamarin,import,mtp,C#,Android,Xamarin,Import,Mtp,我正在修改一个现有的应用程序,它是用C#in Xamarin编写的,专门为Android构建的 现有的应用程序在摄像头的wifi卡上搜索文件,并通过HTTP接口下载。我试图让它下载的文件使用拴在一个USB电缆代替,但无法得到文件下载 我正在使用MtpDevice尝试下载,但ImportFile函数总是失败。不幸的是,它从未抛出异常,也没有给出任何关于原因的有用信息 下面的代码显示了我正在做的事情,尽管我已经删除了一些与问题无关的东西 当我修改现有的应用程序时,代码有点复杂,因为有一种方法可以找到
public async Task<List<MyFileClass>> GetFileListAsync()
{
var results = new List<MyFileClass>();
UsbDeviceConnection usbDeviceConnection = null;
MtpDevice mtpDevice = null;
UsbDevice usbDevice = null;
// all this stuff works as expected...
UsbManager usbManager = (UsbManager)Android.App.Application.Context.GetSystemService(Context.UsbService);
try
{
if (usbManager.DeviceList != null && usbManager.DeviceList.Count > 0)
{
foreach (var usbAccessory in usbManager.DeviceList)
{
var device = usbAccessory.Value;
usbDevice = usbAccessory.Value;
break; // should only ever be one, but break here anyway
}
}
}
catch (Exception ex)
{
Log.Error(ex, "Error getting USB devices");
}
if (usbDevice == null)
{
Log.Information("ConnectedDevice is null");
return false;
}
if (!UsbManager.HasPermission(usbDevice))
{
Log.Information("Requesting permission must have failed");
return false;
}
try
{
usbDeviceConnection = UsbManager.OpenDevice(usbDevice);
}
catch (Exception ex)
{
Log.Error(ex, "opening usb connection");
return false;
}
try
{
mtpDevice = new MtpDevice(usbDevice);
}
catch (Exception ex)
{
Log.Error(ex, "creating mtpdevice");
return false;
}
try
{
mtpDevice.Open(usbDeviceConnection);
}
catch (Exception ex)
{
Log.Error(ex, " opening mtpdevice");
usbDeviceConnection.Close();
return false;
}
// then start looking for files
var storageUnits = mtpDevice.GetStorageIds();
if (storageUnits == null || storageUnits.Length == 0)
{
Log.Information("StorageUnits is empty");
mtpDevice.Close();
usbDeviceConnection.Close();
return false;
}
foreach (var storageUnitId in storageUnits)
{
var storageUnit = mtpDevice.GetStorageInfo(storageUnitId);
if (storageUnit != null)
{
// recurse directories to get list of files
await RecurseMTPDirectories(results, storageUnitId, docketId, docket, db);
}
}
}
private async Task RecurseMTPDirectories(List<MyFileClass> files, int storageUnitId, string parentFolder = "\\", int parentHandle = -1)
{
var results = new List<FlashAirFile>();
var directoryObjects = new List<MtpObjectInfo>();
var objectHandles = mtpDevice.GetObjectHandles(storageUnitId, 0, parentHandle);
if (objectHandles != null && objectHandles.Length > 0)
{
foreach (var objectHandle in objectHandles)
{
MtpObjectInfo objectInfo = mtpDevice.GetObjectInfo(objectHandle);
if (objectInfo != null)
{
if (objectInfo.Format == MtpFormat.Association)
{
// add to the list to recurse into, but do this at the end
directoryObjects.Add(objectInfo);
}
else
{
// it' a file - add it only if it's one we want
try
{
var file = new MyFileClass()
{
TotalBytes = objectInfo.CompressedSize,
FileNameOnCard = objectInfo.Name,
DirectoryOnCard = parentFolder,
MTPHandle = objectHandle
};
}
catch (Exception e)
{
Log.Error(e, " trying to create MTP FlashAirFile");
}
}
}
}
}
foreach (var directoryObject in directoryObjects)
{
string fullname = parentFolder + directoryObject.Name;
await RecurseMTPDirectories(files, storageUnitId, $"{fullname}\\", directoryObject.ObjectHandle);
}
return results;
}
公共异步任务GetFileListAsync()
{
var results=新列表();
UsbDeviceConnection UsbDeviceConnection=null;
MtpDevice MtpDevice=null;
UsbDevice UsbDevice=null;
//所有这些东西都像预期的那样工作。。。
UsbManager UsbManager=(UsbManager)Android.App.Application.Context.GetSystemService(Context.UsbService);
尝试
{
if(usbManager.DeviceList!=null&&usbManager.DeviceList.Count>0)
{
foreach(usbManager.DeviceList中的var usbAccessory)
{
var设备=usbAccessory.Value;
usbDevice=usbAccessory.Value;
break;//应该只有一个,但无论如何在这里break
}
}
}
捕获(例外情况除外)
{
Log.Error(例如,“获取USB设备时出错”);
}
如果(usbDevice==null)
{
日志信息(“ConnectedDevice为空”);
返回false;
}
如果(!UsbManager.HasPermission(usbDevice))
{
日志信息(“请求权限必须失败”);
返回false;
}
尝试
{
usbDeviceConnection=UsbManager.OpenDevice(usbDevice);
}
捕获(例外情况除外)
{
日志错误(例如,“打开usb连接”);
返回false;
}
尝试
{
MTP设备=新的MTP设备(USB设备);
}
捕获(例外情况除外)
{
日志错误(例如,“创建mtpdevice”);
返回false;
}
尝试
{
MTP设备打开(usbDeviceConnection);
}
捕获(例外情况除外)
{
日志错误(例如,“打开MTP设备”);
usbDeviceConnection.Close();
返回false;
}
//然后开始查找文件
var storageUnits=mtpDevice.GetStorageIds();
if(storageUnits==null | | storageUnits.Length==0)
{
日志信息(“存储单元为空”);
mtpDevice.Close();
usbDeviceConnection.Close();
返回false;
}
foreach(storageUnits中的var storageUnitId)
{
var storageUnit=mtpDevice.GetStorageInfo(storageUnitId);
if(storageUnit!=null)
{
//递归目录以获取文件列表
等待RecurseMTP目录(结果、storageUnitId、docketId、docket、db);
}
}
}
私有异步任务RecurseMTP目录(列表文件,int-storageUnitId,字符串parentFolder=“\\”,int-parentHandle=-1)
{
var results=新列表();
var directoryObjects=新列表();
var objectHandles=mtpDevice.GetObjectHandles(storageUnitId,0,parentHandle);
if(objectHandles!=null&&objectHandles.Length>0)
{
foreach(objectHandles中的var objectHandle)
{
MtpObjectInfo objectInfo=mtpDevice.GetObjectInfo(objectHandle);
if(objectInfo!=null)
{
if(objectInfo.Format==MtpFormat.Association)
{
//添加到要递归到的列表中,但在末尾执行此操作
添加(objectInfo);
}
其他的
{
//它是一个文件-仅当它是我们想要的文件时才添加它
尝试
{
var file=新的MyFileClass()
{
TotalBytes=objectInfo.CompressedSize,
filenameocard=objectInfo.Name,
DirectoryOnCard=parentFolder,
MTPHandle=objectHandle
};
}
捕获(例外e)
{
Log.Error(例如,“尝试创建MTP FlashAirFile”);
}
}
}
}
}
foreach(directoryObjects中的var directoryObject)
{
字符串fullname=parentFolder+directoryObject.Name;
等待RecurseMTP目录(文件,storageUnitId,$“{fullname}\\”,directoryObject.ObjectHandle);
}
返回结果;
}
我知道可以一次获取所有句柄,而不是在文件夹中递归,但现在我正在按照旧代码那样做
MyFileClass对象的列表被添加到SQLite数据库中,然后后台线程一次对它们进行一个解队列,并调用DownloadFileAsync来获取每个文件。
此方法使用的设备与GetFileListAsync方法中使用的设备相同,并且它还检查权限是否仍然可用
public async Task<int> DownloadFileAsync(MyFileClass file, string destination)
{
int receivedBytes = 0;
int objectHandle = file.MTPHandle;
connectedDevice = await GetAttachedDevice();
if (connectedDevice == null || !UsbManager.HasPermission(connectedDevice))
return receivedBytes;
if (!await OpenAttachedDevice())
return receivedBytes;
var rootFolder = await FileSystem.Current.GetFolderFromPathAsync(destination);
var localFile = rootFolder.Path;
try
{
Log.Information($"Attempting to download ID {objectHandle} to {localFile}");
// try downloading just using path
bool success = mtpDevice.ImportFile(objectHandle, localFile);
if (!success)
{
// try it with a / on the end of the path
localFile += '/';
Log.Information($"Attempting to download ID {file.DownloadManagerId} to {localFile}");
success = mtpDevice.ImportFile(objectHandle, localFile);
}
if (!success)
{
// try it with the filename on the end of the path as well
localFile += file.FileNameOnSdCard;
Log.Information($"Attempting to download ID {file.DownloadManagerId} to {localFile}");
success = mtpDevice.ImportFile(objectHandle, localFile);
}
if (!success)
{
throw new Exception($"mtpDevice.ImportFile failed for {file.FileNameOnSdCard}");
}
// do stuff here to handle success
}
catch (Exception ex) when (ex is OperationCanceledException || ex is TaskCanceledException)
{
// do some other stuff here in the database
//rethrow the exception so it can be handled further up the chain.
throw;
}
return receivedBytes;
}
公共异步任务下载FileAsync(MyFileClass文件,字符串目标)
{
int receivedBytes=0;
int objectHandle=file.MTPHandle;
connectedDevice=等待GetAttachedDevice();
如果(connectedDevice==null | |!UsbManager.HasPermission(连接