Операция перемещения безумно медленная, и я не могу найти решение, чтобы заставить ее работать быстрее
Может быть много причин, почему перемещение файла занимает так много времени. Например, несколько приложений: приложения для защиты от вредоносных программ - могут сканировать файл, загружать сеть (при перемещении на другой том / диск), сам размер файла, а также возможные запахи кода.
Мне кажется, яПодумайте, вы пошли тем же путем, что и с вашим кодом, чтобы вы могли справиться с тем, сколько всего было перемещено до сих пор, это хорошо, но есть альтернатив , которые могут перемещать эти файлы просто отлично и намного быстрее.
Несколько опций
- Метод System.IO.File.Move () - это работает хорошо, но вы этого не делаетеиметь контроль над прогрессом либо. Под hood он фактически вызывает функцию:
Win32Native.MoveFile
c ++, которая прекрасно работает. - FileInfo.MoveTo - это просто завершает делегирование его работы
Win32.MoveFile
. - Ваш путь - Использование некоторых функций из
Kernel32.dll
- это обеспечивает полный контроль, прогресс в скважине и т. Д. *
Я вернусь к этому выше через минуту, так как я хотел бы коснуться того, что вы первоначально опубликовали о том, как прогресс не обновлялся ранее.
Этот звонок здесь await Task.Run(() => { mover.Copy(); });
будет ожидаться, пока он не будетзавершено, но вы регистрируете событие после этого, например: mover.OnProgressChanged += percentage =>
- после вызова Copy()
, поэтому нет, вы не получите никаких изменений.
Даже если вы получили изменения, у вас будет исключениев любом случае, потому что вы не в потоке пользовательского интерфейса, а в другом потоке. Например:
mover.OnProgressChanged += percentage =>
{
MoveProgress.Value = percentage;
InputButtonText.Text = percentage.ToString();
};
Вы пытаетесь обновить пользовательский интерфейс (progressbar.value) из другого потока, вы просто не можете этого сделать. Чтобы обойти это, вам нужно вызвать из Dispatcher
. Например:
Application.Current.Dispatcher.Invoke(() =>
{
pbProgress.Value = percentage;
});
Назад к файловым операциям
Честно говоря, вы все равно можете делать то, что хотите, таким, какой вы есть, просто переместите несколько вещей вокруги тебе должно быть хорошо. В противном случае ниже я написал класс, в котором вы можете использовать класс, который будет перемещать файл, сообщать о ходе выполнения и т. Д., См. Ниже.
Примечание. Я протестировал файл размером 500 МБ, и он переместился за 2,78 секунды и 850 МБ. файл за 3,37 секунды с локального диска на другой том.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Transactions; // must add reference to System.Transactions
public class FileHelper
{
#region | Public Events |
/// <summary>
/// Occurs when any progress changes occur with file.
/// </summary>
public event ProgressChangeDelegate OnProgressChanged;
/// <summary>
/// Occurs when file process has been completed.
/// </summary>
public event OnCompleteDelegate OnComplete;
#endregion
#region | Enums |
[Flags]
enum MoveFileFlags : uint
{
MOVE_FILE_REPLACE_EXISTSING = 0x00000001,
MOVE_FILE_COPY_ALLOWED = 0x00000002,
MOVE_FILE_DELAY_UNTIL_REBOOT = 0x00000004,
MOVE_FILE_WRITE_THROUGH = 0x00000008,
MOVE_FILE_CREATE_HARDLINK = 0x00000010,
MOVE_FILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}
enum CopyProgressResult : uint
{
PROGRESS_CONTINUE = 0,
PROGRESS_CANCEL = 1,
PROGRESS_STOP = 2,
PROGRESS_QUIET = 3,
}
enum CopyProgressCallbackReason : uint
{
CALLBACK_CHUNK_FINISHED = 0x00000000,
CALLBACK_STREAM_SWITCH = 0x00000001
}
#endregion
#region | Delegates |
private delegate CopyProgressResult CopyProgressRoutine(
long TotalFileSize,
long TotalBytesTransferred,
long StreamSize,
long StreamBytesTransferred,
uint dwStreamNumber,
CopyProgressCallbackReason dwCallbackReason,
IntPtr hSourceFile,
IntPtr hDestinationFile,
IntPtr lpData);
public delegate void ProgressChangeDelegate(double percentage);
public delegate void OnCompleteDelegate(bool completed);
#endregion
#region | Imports |
[DllImport("Kernel32.dll")]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("Kernel32.dll")]
private static extern bool MoveFileTransactedW([MarshalAs(UnmanagedType.LPWStr)]string existingfile, [MarshalAs(UnmanagedType.LPWStr)]string newfile,
IntPtr progress, IntPtr lpData, IntPtr flags, IntPtr transaction);
[DllImport("Kernel32.dll")]
private static extern bool MoveFileWithProgressA(string existingfile, string newfile,
CopyProgressRoutine progressRoutine, IntPtr lpData, MoveFileFlags flags);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
private interface IKernelTransaction
{
void GetHandle([Out] out IntPtr handle);
}
#endregion
#region | Public Routines |
/// <summary>
/// Will attempt to move a file using a transaction, if successful then the source file will be deleted.
/// </summary>
/// <param name="existingFile"></param>
/// <param name="newFile"></param>
/// <returns></returns>
public static bool MoveFileTransacted(string existingFile, string newFile)
{
bool success = true;
using (TransactionScope tx = new TransactionScope())
{
if (Transaction.Current != null)
{
IKernelTransaction kt = (IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current);
IntPtr txh;
kt.GetHandle(out txh);
if (txh == IntPtr.Zero) { success = false; return success; }
success = MoveFileTransactedW(existingFile, newFile, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, txh);
if (success)
{
tx.Complete();
}
CloseHandle(txh);
}
else
{
try
{
File.Move(existingFile, newFile);
return success;
}
catch (Exception ex) { success = false; }
}
return success;
}
}
/// <summary>
/// Attempts to move a file from one destination to another. If it succeeds, then the source
/// file is deleted after successful move.
/// </summary>
/// <param name="fileToMove"></param>
/// <param name="newFilePath"></param>
/// <returns></returns>
public async Task<bool> MoveFileAsyncWithProgress(string fileToMove, string newFilePath)
{
bool success = false;
try
{
await Task.Run(() =>
{
success = MoveFileWithProgressA(fileToMove, newFilePath, new CopyProgressRoutine(CopyProgressHandler), IntPtr.Zero, MoveFileFlags .MOVE_FILE_REPLACE_EXISTSING|MoveFileFlags.MOVE_FILE_WRITE_THROUGH|MoveFileFlags.MOVE_FILE_COPY_ALLOWED);
});
}
catch (Exception ex)
{
success = false;
}
finally
{
OnComplete(success);
}
return success;
}
private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long StreamByteTrans, uint dwStreamNumber,CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
{
double percentage = transferred * 100.0 / total;
OnProgressChanged(percentage);
return CopyProgressResult.PROGRESS_CONTINUE;
}
#endregion
}
Как использовать
Один пример -
// Just a public property to hold an instance we need
public FileHelper FileHelper { get; set; }
Вклзагрузить зарегистрировать события ...
FileHelper = new FileHelper();
FileHelper.OnProgressChanged += FileHelper_OnProgressChanged;
FileHelper.OnComplete += FileHelper_OnComplete;
Вот логика ...
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool success = await FileHelper.MoveFileAsyncWithProgress("FILETOMOVE", "DestinationFilePath");
}
// This is called when progress changes, if file is small, it
// may not even hit this.
private void FileHelper_OnProgressChanged(double percentage)
{
Application.Current.Dispatcher.Invoke(() =>
{
pbProgress.Value = percentage;
});
}
// This is called after a move, whether it succeeds or not
private void FileHelper_OnComplete(bool completed)
{
Application.Current.Dispatcher.Invoke(() =>
{
MessageBox.Show("File process succeded: " + completed.ToString());
});
}
* Примечание: в этом вспомогательном классе есть другая функция, MoveFileTransacted
, вы действительно неЭто нужно, это еще одна вспомогательная функция, которая позволяет вам перемещать файл с помощью транзакции;если происходит исключение, файл не перемещается и т.д ...