/// <summary>
		/// Get a list of all files recursively in a specified directory in FlashAir card.
		/// </summary>
		/// <param name="client">HttpClient</param>
		/// <param name="remoteDirectoryPath">Remote directory path</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>File list</returns>
		/// <remarks>This method is part of parent method.</remarks>
		private static async Task<List<IFileItem>> GetFileListAllAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
		{
			var itemList = await GetFileListEachAsync(client, remoteDirectoryPath, card, cancellationToken);

			for (int i = itemList.Count - 1; 0 <= i; i--)
			{
				if (itemList[i].IsHidden || itemList[i].IsSystemFile || itemList[i].IsVolume ||
					itemList[i].IsFlashAirSystemFolder)
				{
					itemList.Remove(itemList[i]);
					continue;
				}

				if (!itemList[i].IsDirectory)
				{
					if (!itemList[i].IsImageFile)
					{
						itemList.Remove(itemList[i]);
					}
					continue;
				}

				var path = itemList[i].FilePath;
				itemList.Remove(itemList[i]);
				itemList.AddRange(await GetFileListAllAsync(client, path, card, cancellationToken));
			}
			return itemList;
		}
Пример #2
0
        /// <summary>
        /// Check if PC is connected to a network and if applicable, a specified wireless LAN.
        /// </summary>
        /// <param name="card">FlashAir card information</param>
        /// <returns>True if connected</returns>
        internal static bool IsNetworkConnected(CardInfo card)
        {
            if (!NetworkInterface.GetIsNetworkAvailable())
                return false;

            if ((card == null) || String.IsNullOrWhiteSpace(card.Ssid) || !card.IsWirelessConnected)
                return true;

            return IsWirelessNetworkConnected(card.Ssid);
        }
		/// <summary>
		/// Get a list of all files recursively from root folder of FlashAir card.
		/// </summary>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>File list</returns>
		internal static async Task<List<IFileItem>> GetFileListRootAsync(CardInfo card, CancellationToken cancellationToken)
		{
			try
			{
				using (var client = new HttpClient { Timeout = _timeoutDuration })
				{
					return await GetFileListAllAsync(client, Settings.Current.RemoteDescendant, card, cancellationToken).ConfigureAwait(false);
				}
			}
			catch
			{
				Debug.WriteLine("Failed to get all file list.");
				throw;
			}
		}
Пример #4
0
 internal Task <byte[]> GetSaveFileAsync(string remoteFilePath, string localFilePath, int size, DateTime itemDate, bool canReadExif, IProgress <ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken) =>
 GetSaveFileAsync(_client, remoteFilePath, localFilePath, size, itemDate, canReadExif, progress, card, cancellationToken);
Пример #5
0
        private static async Task <byte[]> DownloadBytesAsync(HttpClient client, string path, int size, IProgress <ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken)
        {
            var timeoutDuration = TimeSpan.FromSeconds(Settings.Current.TimeoutDuration);
            int retryCount      = 0;

            while (true)
            {
                retryCount++;

                try
                {
                    try
                    {
                        using (var response = await client.GetAsync(path, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
                        {
                            // If HttpResponseMessage.EnsureSuccessStatusCode is set, an exception by this setting
                            // will be thrown in the scope of HttpClient and so cannot be caught in this method.
                            switch (response.StatusCode)
                            {
                            case HttpStatusCode.OK:
                                // None.
                                break;

                            case HttpStatusCode.Unauthorized:
                            case HttpStatusCode.InternalServerError:
                            case HttpStatusCode.BadRequest:
                                throw new RemoteConnectionUnableException(response.StatusCode);

                            case HttpStatusCode.NotFound:
                                // This status code does not always mean that the specified file is missing.
                                throw new RemoteFileNotFoundException("File is missing or request cannot be handled!", path);

                            default:
                                throw new HttpRequestException($"StatusCode: {response.StatusCode}");
                            }

                            if ((0 < size) &&
                                (response.Content.Headers.ContentLength != size))
                            {
                                throw new RemoteFileInvalidException("Data length does not match!", path);
                            }

                            // Because of HttpCompletionOption.ResponseHeadersRead option, neither CancellationToken
                            // nor HttpClient.Timeout setting works for response content.

                            // Register delegate to CancellationToken because CancellationToken can no longer
                            // directly affect HttpClient. Disposing the HttpResponseMessage will make ReadAsStreamAsync
                            // method throw an ObjectDisposedException and so exit this operation.
                            var ctr = new CancellationTokenRegistration();
                            try
                            {
                                ctr = cancellationToken.Register(() => response.Dispose());
                            }
                            catch (ObjectDisposedException ode)
                            {
                                // If CancellationTokenSource has been disposed during operation (it unlikely happens),
                                // this exception will be thrown.
                                Debug.WriteLine($"CancellationTokenSource has been disposed when tried to register delegate.\r\n{ode}");
                            }
                            using (ctr)
                            {
                                var tcs = new TaskCompletionSource <bool>();

                                // Start timer to monitor network connection.
                                using (var monitorTimer = new Timer(s =>
                                {
                                    if (!NetworkChecker.IsNetworkConnected(card))
                                    {
                                        ((TaskCompletionSource <bool>)s).TrySetResult(true);
                                    }
                                }, tcs, _monitorInterval, _monitorInterval))
                                {
                                    var monitorTask = tcs.Task;

                                    if ((size == 0) || (progress == null))
                                    {
                                        // Route without progress reporting
                                        var readTask    = Task.Run(async() => await response.Content.ReadAsByteArrayAsync());
                                        var timeoutTask = Task.Delay(timeoutDuration);

                                        var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask);

                                        if (completedTask == timeoutTask)
                                        {
                                            throw new TimeoutException("Reading response content timed out!");
                                        }
                                        if (completedTask == monitorTask)
                                        {
                                            throw new RemoteConnectionLostException("Connection lost!");
                                        }

                                        var bytes = await readTask;

                                        if ((0 < size) && (bytes.Length != size))
                                        {
                                            throw new RemoteFileInvalidException("Data length does not match!", path);
                                        }

                                        return(bytes);
                                    }
                                    else
                                    {
                                        // Route with progress reporting
                                        int readLength;
                                        int readLengthTotal = 0;

                                        var buffer      = new byte[65536];                                    // 64KiB
                                        var bufferTotal = new byte[size];

                                        const double stepUint  = 524288D;                                        // 512KiB
                                        double       stepTotal = Math.Ceiling(size / stepUint);                  // The number of steps to report during downloading
                                        if (stepTotal < 6)
                                        {
                                            stepTotal = 6;                                             // The minimum number of steps
                                        }
                                        double stepCurrent = 1D;
                                        var    startTime   = DateTime.Now;

                                        using (var stream = await response.Content.ReadAsStreamAsync())
                                        {
                                            while (readLengthTotal != size)
                                            {
                                                // CancellationToken in overload of ReadAsync method will not work for response content.
                                                var readTask    = Task.Run(async() => await stream.ReadAsync(buffer, 0, buffer.Length));
                                                var timeoutTask = Task.Delay(timeoutDuration);

                                                var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask);

                                                if (completedTask == timeoutTask)
                                                {
                                                    throw new TimeoutException("Reading response content timed out!");
                                                }
                                                if (completedTask == monitorTask)
                                                {
                                                    throw new RemoteConnectionLostException("Connection lost!");
                                                }

                                                readLength = await readTask;

                                                if ((readLength == 0) || (readLengthTotal + readLength > size))
                                                {
                                                    throw new RemoteFileInvalidException("Data length does not match!", path);
                                                }

                                                Buffer.BlockCopy(buffer, 0, bufferTotal, readLengthTotal, readLength);

                                                readLengthTotal += readLength;

                                                monitorTimer.Change(_monitorInterval, _monitorInterval);

                                                // Report if read length in total exceeds stepped length.
                                                if (stepCurrent / stepTotal * size <= readLengthTotal)
                                                {
                                                    progress.Report(new ProgressInfo(
                                                                        currentValue: readLengthTotal,
                                                                        totalValue: size,
                                                                        elapsedTime: DateTime.Now - startTime,
                                                                        isFirst: stepCurrent == 1D));

                                                    stepCurrent++;
                                                }
                                            }
                                        }
                                        return(bufferTotal);
                                    }
                                }
                            }
                        }
                    }
                    catch (OperationCanceledException)                     // Including TaskCanceledException
                    {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            // If cancellation has not been requested, the reason of this exception must be timeout.
                            // This is for response header only.
                            throw new TimeoutException("Reading response header timed out!");
                        }
                        throw;
                    }
                    catch (ObjectDisposedException)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            // If cancellation has been requested, the reason of this exception must be cancellation.
                            // This is for response content only.
                            throw new OperationCanceledException();
                        }
                        throw;
                    }
                    catch (IOException ie)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            var we = ie.InnerException as WebException;
                            if (we?.Status == WebExceptionStatus.ConnectionClosed)
                            {
                                // If cancellation has been requested during downloading, this exception may be thrown.
                                throw new OperationCanceledException();
                            }
                        }
                        throw;
                    }
                    catch (HttpRequestException hre)
                    {
                        var we = hre.InnerException as WebException;
                        if (we != null)
                        {
                            // If unable to connect to FlashAir card, this exception will be thrown.
                            // The status may vary, such as WebExceptionStatus.NameResolutionFailure,
                            // WebExceptionStatus.ConnectFailure.
                            throw new RemoteConnectionUnableException(we.Status);
                        }

                        var ode = hre.InnerException as ObjectDisposedException;
                        if (ode != null)
                        {
                            // If lost connection to FlashAir card, this exception may be thrown.
                            // Error message: Error while copying content to a stream.
                            throw new RemoteConnectionLostException("Connection lost!");
                        }
                        throw;
                    }
                }
                catch (RemoteConnectionUnableException)
                {
                    if (retryCount >= _retryCountMax)
                    {
                        throw;
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Failed to download byte array.\r\n{ex}");
                    throw;
                }

                // Wait interval before retry.
                if (TimeSpan.Zero < _retryInterval)
                {
                    await Task.Delay(_retryInterval, cancellationToken);
                }
            }
        }
Пример #6
0
 internal Task <int> GetFileNumAsync(string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken) =>
 GetFileNumAsync(_client, remoteDirectoryPath, card, cancellationToken);
Пример #7
0
 internal Task <IEnumerable <IFileItem> > GetFileListAsync(string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken) =>
 GetFileListAsync(_client, remoteDirectoryPath, card, cancellationToken);
Пример #8
0
        /// <summary>
        /// Gets file data of a specified remote file in FlashAir card and save it in local folder.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteFilePath">Remote file path</param>
        /// <param name="localFilePath">Local file path</param>
        /// <param name="size">File size provided by FlashAir card</param>
        /// <param name="itemDate">Date provided by FlashAir card</param>
        /// <param name="canReadExif">Whether can read Exif metadata from the file</param>
        /// <param name="progress">Progress</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>Byte array of file</returns>
        internal static async Task <byte[]> GetSaveFileAsync(HttpClient client, string remoteFilePath, string localFilePath, int size, DateTime itemDate, bool canReadExif, IProgress <ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(remoteFilePath))
            {
                throw new ArgumentNullException(nameof(remoteFilePath));
            }

            if (string.IsNullOrWhiteSpace(localFilePath))
            {
                throw new ArgumentNullException(nameof(localFilePath));
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.None, remoteFilePath);

            try
            {
                var bytes = await DownloadBytesAsync(client, remotePath, size, progress, card, cancellationToken).ConfigureAwait(false);

                using (var fs = new FileStream(localFilePath, FileMode.Create, FileAccess.Write))
                {
                    await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
                }

                // Conform date of copied file in local folder to that of original file in FlashAir card.
                var localFileInfo = new FileInfo(localFilePath);
                localFileInfo.CreationTime  = itemDate;                // Creation time
                localFileInfo.LastWriteTime = itemDate;                // Last write time

                // Overwrite creation time of copied file by date of image taken from Exif metadata.
                if (canReadExif)
                {
                    var exifDateTaken = await ImageManager.GetExifDateTakenAsync(bytes);

                    if (exifDateTaken != default(DateTime))
                    {
                        localFileInfo.CreationTime = exifDateTaken;
                    }
                }

                return(bytes);
            }
            catch
            {
                Debug.WriteLine("Failed to get and save a file.");
                throw;
            }
        }
Пример #9
0
        /// <summary>
        /// Gets the number of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>The number of files</returns>
        /// <remarks>This method is not actually used.</remarks>
        internal static async Task <int> GetFileNumAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(remoteDirectoryPath))
            {
                throw new ArgumentNullException(nameof(remoteDirectoryPath));
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileNum, remoteDirectoryPath);

            try
            {
                var fileNum = await DownloadStringAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

                int num;
                return(int.TryParse(fileNum, out num) ? num : 0);
            }
            catch
            {
                Debug.WriteLine("Failed to get the number of files.");
                throw;
            }
        }
Пример #10
0
        /// <summary>
        /// Gets a list of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>File list</returns>
        /// <remarks>This method is part of parent method.</remarks>
        private static async Task <List <IFileItem> > GetFileListEachAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
        {
            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

            var fileEntries = await DownloadStringAsync(client, remotePath, card, cancellationToken);

            return(fileEntries.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                   .Select <string, IFileItem>(fileEntry => new FileItem(fileEntry, remoteDirectoryPath))
                   .Where(x => x.IsImported)
                   .ToList());
        }
		/// <summary>
		/// Get the number of files in a specified directory in FlashAir card.
		/// </summary>
		/// <param name="remoteDirectoryPath">Remote directory path</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>The number of files</returns>
		/// <remarks>This method is not actually used.</remarks>
		internal static async Task<int> GetFileNumAsync(string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
		{
			if (String.IsNullOrWhiteSpace(remoteDirectoryPath))
				throw new ArgumentNullException("remoteDirectoryPath");

			var remotePath = ComposeRemotePath(FileManagerCommand.GetFileNum, remoteDirectoryPath);

			try
			{
				using (var client = new HttpClient { Timeout = _timeoutDuration })
				{
					var fileNum = await DownloadStringAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

					int num;
					return int.TryParse(fileNum, out num) ? num : 0;
				}
			}
			catch
			{
				Debug.WriteLine("Failed to get the number of files.");
				throw;
			}
		}
		/// <summary>
		/// Get a list of files in a specified directory in FlashAir card.
		/// </summary>
		/// <param name="remoteDirectoryPath">Remote directory path</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>File list</returns>
		/// <remarks>This method is not actually used.</remarks>
		internal static async Task<List<IFileItem>> GetFileListAsync(string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
		{
			if (String.IsNullOrWhiteSpace(remoteDirectoryPath))
				throw new ArgumentNullException("remoteDirectoryPath");

			var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

			try
			{
				using (var client = new HttpClient { Timeout = _timeoutDuration })
				{
					var fileEntries = await DownloadStringAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

					return fileEntries.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
						.Select<string, IFileItem>(fileEntry => new FileItem(fileEntry, remoteDirectoryPath))
						.Where(x => x.IsImported)
						.ToList();
				}
			}
			catch
			{
				Debug.WriteLine("Failed to get file list.");
				throw;
			}
		}
		/// <summary>
		/// Get a list of files in a specified directory in FlashAir card.
		/// </summary>
		/// <param name="client">HttpClient</param>
		/// <param name="remoteDirectoryPath">Remote directory path</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>File list</returns>
		/// <remarks>This method is part of parent method.</remarks>
		private static async Task<List<IFileItem>> GetFileListEachAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
		{
			var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

			var fileEntries = await DownloadStringAsync(client, remotePath, card, cancellationToken);

			return fileEntries.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
				.Select<string, IFileItem>(fileEntry => new FileItem(fileEntry, remoteDirectoryPath))
				.Where(x => x.IsImported)
				.ToList();
		}
Пример #14
0
        /// <summary>
        /// Gets file data of a specified remote file in FlashAir card and save it in local folder.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteFilePath">Remote file path</param>
        /// <param name="localFilePath">Local file path</param>
        /// <param name="size">File size provided by FlashAir card</param>
        /// <param name="itemDate">Date provided by FlashAir card</param>
        /// <param name="canReadExif">Whether can read Exif metadata from the file</param>
        /// <param name="progress">Progress</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>Byte array of file</returns>
        internal static async Task <byte[]> GetSaveFileAsync(HttpClient client, string remoteFilePath, string localFilePath, int size, DateTime itemDate, bool canReadExif, IProgress <ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(remoteFilePath))
            {
                throw new ArgumentNullException(nameof(remoteFilePath));
            }

            if (string.IsNullOrWhiteSpace(localFilePath))
            {
                throw new ArgumentNullException(nameof(localFilePath));
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.None, remoteFilePath);

            byte[] bytes = null;

            try
            {
                bytes = await DownloadBytesAsync(client, remotePath, size, progress, card, cancellationToken).ConfigureAwait(false);
            }
            catch
            {
                Debug.WriteLine("Failed to get a file.");
                throw;
            }

            int retryCount = 0;

            while (true)
            {
                try
                {
                    using (var fs = new FileStream(localFilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
                    {
                        var creationTime  = itemDate;
                        var lastWriteTime = itemDate;

                        // Overwrite creation time by date of image taken from Exif metadata.
                        if (canReadExif)
                        {
                            var exifDateTaken = await ImageManager.GetExifDateTakenAsync(bytes, DateTimeKind.Local);

                            if (exifDateTaken != default(DateTime))
                            {
                                creationTime = exifDateTaken;
                            }
                        }

                        FileTime.SetFileTime(fs.SafeFileHandle, creationTime: creationTime, lastWriteTime: lastWriteTime);

                        await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
                    }
                    return(bytes);
                }
                catch (IOException) when(++retryCount < MaxRetryCount)
                {
                    // Wait interval before retry.
                    if (TimeSpan.Zero < _retryInterval)
                    {
                        await Task.Delay(_retryInterval, cancellationToken);
                    }
                }
                catch
                {
                    Debug.WriteLine("Failed to save a file.");
                    throw;
                }
            }
        }
Пример #15
0
 private static async Task <byte[]> DownloadBytesAsync(HttpClient client, string path, CancellationToken token, CardInfo card)
 {
     return(await DownloadBytesAsync(client, path, 0, null, token, card));
 }
Пример #16
0
        private static async Task <string> DownloadStringAsync(HttpClient client, string path, CancellationToken token, CardInfo card)
        {
            var bytes = await DownloadBytesAsync(client, path, 0, null, token, card);

            if (recordsDownloadString)
            {
                await RecordDownloadStringAsync(path, bytes);
            }

            // Response from FlashAir card seems to be ASCII encoded. Not certain though.
            return(Encoding.ASCII.GetString(bytes));
        }
Пример #17
0
 /// <summary>
 /// Get a list of all files recursively from root of FlashAir card.
 /// </summary>
 /// <param name="token">CancellationToken</param>
 /// <param name="card">FlashAir card information</param>
 internal static async Task <List <FileItemViewModel> > GetFileListRootAsync(CancellationToken token, CardInfo card)
 {
     try
     {
         using (var client = new HttpClient()
         {
             Timeout = timeoutLength
         })
         {
             return(await GetFileListAllAsync(client, String.Empty, token, card).ConfigureAwait(false));
         }
     }
     catch
     {
         Debug.WriteLine("Failed to get all file list.");
         throw;
     }
 }
Пример #18
0
        /// <summary>
        /// Get number of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="token">CancellationToken</param>
        /// <param name="card">FlashAir card information</param>
        /// <remarks>This method is not actually used.</remarks>
        internal static async Task <int> GetFileNumAsync(string remoteDirectoryPath, CancellationToken token, CardInfo card)
        {
            if (String.IsNullOrWhiteSpace(remoteDirectoryPath))
            {
                throw new ArgumentNullException("remoteDirectoryPath");
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileNum, remoteDirectoryPath);

            try
            {
                using (var client = new HttpClient()
                {
                    Timeout = timeoutLength
                })
                {
                    var itemNum = await DownloadStringAsync(client, remotePath, token, card).ConfigureAwait(false);

                    int num;
                    return(int.TryParse(itemNum, out num) ? num : 0);
                }
            }
            catch
            {
                Debug.WriteLine("Failed to get the number of files.");
                throw;
            }
        }
Пример #19
0
        /// <summary>
        /// Gets a list of all files recursively in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>File list</returns>
        /// <remarks>This method is part of parent method.</remarks>
        private static async Task <List <IFileItem> > GetFileListAllAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
        {
            var itemList = await GetFileListEachAsync(client, remoteDirectoryPath, card, cancellationToken);

            for (int i = itemList.Count - 1; 0 <= i; i--)
            {
                if (itemList[i].IsHidden || itemList[i].IsSystemFile || itemList[i].IsVolume ||
                    itemList[i].IsFlashAirSystemFolder)
                {
                    itemList.RemoveAt(i);
                    continue;
                }

                if (!itemList[i].IsDirectory)
                {
                    if (!itemList[i].IsImageFile)
                    {
                        itemList.RemoveAt(i);
                    }
                    continue;
                }

                var path = itemList[i].FilePath;
                itemList.RemoveAt(i);
                itemList.AddRange(await GetFileListAllAsync(client, path, card, cancellationToken));
            }
            return(itemList);
        }
		/// <summary>
		/// Get a thumbnail of a specified image file in FlashAir card.
		/// </summary>
		/// <param name="remoteFilePath">Remote file path</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>Thumbnail of image file</returns>
		internal static async Task<BitmapSource> GetThumbnailAsync(string remoteFilePath, CardInfo card, CancellationToken cancellationToken)
		{
			if (String.IsNullOrWhiteSpace(remoteFilePath))
				throw new ArgumentNullException("remoteFilePath");

			var remotePath = ComposeRemotePath(FileManagerCommand.GetThumbnail, remoteFilePath);

			try
			{
				using (var client = new HttpClient { Timeout = _timeoutDuration })
				{
					var bytes = await DownloadBytesAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

					return await ImageManager.ConvertBytesToBitmapSourceAsync(bytes).ConfigureAwait(false);
				}
			}
			catch (ImageNotSupportedException)
			{
				// This exception should not be thrown because thumbnail data is directly provided by FlashAir card.
				return null;
			}
			catch (Exception ex)
			{
				if ((ex.GetType() == typeof(RemoteFileNotFoundException)) ||
					((ex.GetType() == typeof(RemoteConnectionUnableException)) &&
					(((RemoteConnectionUnableException)ex).Code == HttpStatusCode.InternalServerError)))
				{
					// If image file is not JPEG format or if there is no Exif standardized thumbnail stored,
					// StatusCode will be HttpStatusCode.NotFound. Or it may be HttpStatusCode.InternalServerError
					// when image file is non-standard JPEG format.
					Debug.WriteLine("Image file may not be JPEG format or may contain no thumbnail.");
					throw new RemoteFileThumbnailFailedException(remotePath);
				}

				Debug.WriteLine("Failed to get a thumbnail.");
				throw;
			}
		}
Пример #21
0
        /// <summary>
        /// Gets a list of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>File list</returns>
        /// <remarks>This method is not actually used.</remarks>
        internal static async Task <IEnumerable <IFileItem> > GetFileListAsync(HttpClient client, string remoteDirectoryPath, CardInfo card, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(remoteDirectoryPath))
            {
                throw new ArgumentNullException(nameof(remoteDirectoryPath));
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

            try
            {
                var fileEntries = await DownloadStringAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

                return(fileEntries.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                       .Select <string, IFileItem>(fileEntry => new FileItem(fileEntry, remoteDirectoryPath))
                       .Where(x => x.IsImported)
                       .ToList());
            }
            catch
            {
                Debug.WriteLine("Failed to get file list.");
                throw;
            }
        }
		/// <summary>
		/// Get file data of a specified remote file in FlashAir card and save it in local folder.
		/// </summary>
		/// <param name="remoteFilePath">Remote file path</param>
		/// <param name="localFilePath">Local file path</param>
		/// <param name="size">File size provided by FlashAir card</param>
		/// <param name="itemDate">Date provided by FlashAir card</param>
		/// <param name="canReadExif">Whether can read Exif metadata from the file</param>
		/// <param name="progress">Progress</param>
		/// <param name="card">FlashAir card information</param>
		/// <param name="cancellationToken">CancellationToken</param>
		/// <returns>Byte array of file</returns>
		internal static async Task<byte[]> GetSaveFileAsync(string remoteFilePath, string localFilePath, int size, DateTime itemDate, bool canReadExif, IProgress<ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken)
		{
			if (String.IsNullOrWhiteSpace(remoteFilePath))
				throw new ArgumentNullException("remoteFilePath");

			if (String.IsNullOrWhiteSpace(localFilePath))
				throw new ArgumentNullException("localFilePath");

			var remotePath = ComposeRemotePath(FileManagerCommand.None, remoteFilePath);

			try
			{
				using (var client = new HttpClient { Timeout = _timeoutDuration })
				{
					var bytes = await DownloadBytesAsync(client, remotePath, size, progress, card, cancellationToken).ConfigureAwait(false);

					using (var fs = new FileStream(localFilePath, FileMode.Create, FileAccess.Write))
					{
						await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
					}

					// Conform date of copied file in local folder to that of original file in FlashAir card.
					var localFileInfo = new FileInfo(localFilePath);
					localFileInfo.CreationTime = itemDate; // Creation time
					localFileInfo.LastWriteTime = itemDate; // Last write time

					// Overwrite creation time of copied file by date of image taken from Exif metadata.
					if (canReadExif)
					{
						var exifDateTaken = await ImageManager.GetExifDateTakenAsync(bytes);
						if (exifDateTaken != default(DateTime))
						{
							localFileInfo.CreationTime = exifDateTaken;
						}
					}

					return bytes;
				}
			}
			catch
			{
				Debug.WriteLine("Failed to get and save a file.");
				throw;
			}
		}
Пример #23
0
        /// <summary>
        /// Gets a thumbnail of a specified image file in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteFilePath">Remote file path</param>
        /// <param name="card">FlashAir card information</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <returns>Thumbnail of image file</returns>
        internal static async Task <BitmapSource> GetThumbnailAsync(HttpClient client, string remoteFilePath, CardInfo card, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(remoteFilePath))
            {
                throw new ArgumentNullException(nameof(remoteFilePath));
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.GetThumbnail, remoteFilePath);

            try
            {
                var bytes = await DownloadBytesAsync(client, remotePath, card, cancellationToken).ConfigureAwait(false);

                return(await ImageManager.ConvertBytesToBitmapSourceAsync(bytes).ConfigureAwait(false));
            }
            catch (ImageNotSupportedException)
            {
                // This exception should not be thrown because thumbnail data is directly provided by FlashAir card.
                return(null);
            }
            catch (Exception ex)
            {
                if ((ex is RemoteFileNotFoundException) ||
                    ((ex is RemoteConnectionUnableException) &&
                     (((RemoteConnectionUnableException)ex).Code == HttpStatusCode.InternalServerError)))
                {
                    // If image file is not JPEG format or if there is no Exif standardized thumbnail stored,
                    // StatusCode will be HttpStatusCode.NotFound. Or it may be HttpStatusCode.InternalServerError
                    // when image file is non-standard JPEG format.
                    Debug.WriteLine("Image file may not be JPEG format or may contain no thumbnail.");
                    throw new RemoteFileThumbnailFailedException(remotePath);
                }

                Debug.WriteLine("Failed to get a thumbnail.");
                throw;
            }
        }
		private static async Task<string> DownloadStringAsync(HttpClient client, string path, CardInfo card, CancellationToken cancellationToken)
		{
			var bytes = await DownloadBytesAsync(client, path, 0, null, card, cancellationToken);

			if (_recordsDownloadString)
				await RecordDownloadStringAsync(path, bytes);

			// Response from FlashAir card seems to be encoded by ASCII.
			return Encoding.ASCII.GetString(bytes);
		}
Пример #25
0
 internal Task <IEnumerable <IFileItem> > GetFileListRootAsync(CardInfo card, CancellationToken cancellationToken) =>
 GetFileListRootAsync(_client, card, cancellationToken);
		private static async Task<byte[]> DownloadBytesAsync(HttpClient client, string path, int size, CardInfo card, CancellationToken cancellationToken)
		{
			return await DownloadBytesAsync(client, path, size, null, card, cancellationToken);
		}
Пример #27
0
        private static async Task <string> DownloadStringAsync(HttpClient client, string path, CardInfo card, CancellationToken cancellationToken)
        {
            var bytes = await DownloadBytesAsync(client, path, 0, null, card, cancellationToken);

            if (_recordsDownloadString)
            {
                await RecordDownloadStringAsync(path, bytes);
            }

            // Response from FlashAir card seems to be encoded by ASCII.
            return(Encoding.ASCII.GetString(bytes));
        }
		private static async Task<byte[]> DownloadBytesAsync(HttpClient client, string path, int size, IProgress<ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken)
		{
			int retryCount = 0;

			while (true)
			{
				retryCount++;

				try
				{
					try
					{
						using (var response = await client.GetAsync(path, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
						{
							// If HttpResponseMessage.EnsureSuccessStatusCode is set, an exception by this setting
							// will be thrown in the scope of HttpClient and so cannot be caught in this method.
							switch (response.StatusCode)
							{
								case HttpStatusCode.OK:
									// None.
									break;
								case HttpStatusCode.Unauthorized:
								case HttpStatusCode.InternalServerError:
								case HttpStatusCode.BadRequest:
									throw new RemoteConnectionUnableException(response.StatusCode);
								case HttpStatusCode.NotFound:
									// This exception does not always mean that the specified file is missing.
									throw new RemoteFileNotFoundException("File is missing or request cannot be handled!", path);
								default:
									throw new HttpRequestException(String.Format("StatusCode: {0}", response.StatusCode));
							}

							if ((0 < size) &&
								(!response.Content.Headers.ContentLength.HasValue ||
								 (response.Content.Headers.ContentLength.Value != size)))
								throw new RemoteFileInvalidException("Data length does not match!", path);

							// Because of HttpCompletionOption.ResponseHeadersRead option, neither CancellationToken
							// nor HttpClient.Timeout setting works for response content.

							// Register delegate to CancellationToken because CancellationToken can no longer
							// directly affect HttpClient. Disposing the HttpResponseMessage will make ReadAsStreamAsync
							// method throw an ObjectDisposedException and so exit this operation.
							var ctr = new CancellationTokenRegistration();
							try
							{
								ctr = cancellationToken.Register(() => response.Dispose());
							}
							catch (ObjectDisposedException ode)
							{
								// If CancellationTokenSource has been disposed during operation (it unlikely happens),
								// this exception will be thrown.
								Debug.WriteLine("CancellationTokenSource has been disposed when tried to register delegate. {0}", ode);
							}
							using (ctr)
							{
								var tcs = new TaskCompletionSource<bool>();

								// Start timer to monitor network connection.
								using (var monitorTimer = new Timer(s =>
								{
									if (!NetworkChecker.IsNetworkConnected(card))
									{
										((TaskCompletionSource<bool>)s).TrySetResult(true);
									}
								}, tcs, _monitorInterval, _monitorInterval))
								{
									var monitorTask = tcs.Task;

									if ((size == 0) || (progress == null))
									{
										// Route without progress reporting
										var readTask = Task.Run(async () => await response.Content.ReadAsByteArrayAsync());
										var timeoutTask = Task.Delay(_timeoutDuration);

										var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask);
										if (completedTask == timeoutTask)
											throw new TimeoutException("Reading response content timed out!");
										if (completedTask == monitorTask)
											throw new RemoteConnectionLostException("Connection lost!");

										var bytes = await readTask;

										if ((0 < size) && (bytes.Length != size))
											throw new RemoteFileInvalidException("Data length does not match!", path);

										return bytes;
									}
									else
									{
										// Route with progress reporting
										int readLength;
										int readLengthTotal = 0;

										var buffer = new byte[65536]; // 64KiB
										var bufferTotal = new byte[size];

										const double stepUint = 524288D; // 512KiB
										double stepTotal = Math.Ceiling(size / stepUint); // The number of steps to report during downloading
										if (stepTotal < 6)
											stepTotal = 6; // The minimum number of steps

										double stepCurrent = 1D;
										var startTime = DateTime.Now;

										using (var stream = await response.Content.ReadAsStreamAsync())
										{
											while (readLengthTotal != size)
											{
												// CancellationToken in overload of ReadAsync method will not work for response content.
												var readTask = Task.Run(async () => await stream.ReadAsync(buffer, 0, buffer.Length));
												var timeoutTask = Task.Delay(_timeoutDuration);

												var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask);
												if (completedTask == timeoutTask)
													throw new TimeoutException("Reading response content timed out!");
												if (completedTask == monitorTask)
													throw new RemoteConnectionLostException("Connection lost!");

												readLength = await readTask;

												if ((readLength == 0) || (readLengthTotal + readLength > size))
													throw new RemoteFileInvalidException("Data length does not match!", path);

												Buffer.BlockCopy(buffer, 0, bufferTotal, readLengthTotal, readLength);

												readLengthTotal += readLength;

												monitorTimer.Change(_monitorInterval, _monitorInterval);

												// Report if read length in total exceeds stepped length.
												if (stepCurrent / stepTotal * size <= readLengthTotal)
												{
													progress.Report(new ProgressInfo(
														currentValue: readLengthTotal,
														totalValue: size,
														elapsedTime: DateTime.Now - startTime,
														isFirst: stepCurrent == 1D));

													stepCurrent++;
												}
											}
										}
										return bufferTotal;
									}
								}
							}
						}
					}
					// Sort out exceptions.					
					catch (OperationCanceledException) // Including TaskCanceledException
					{
						if (!cancellationToken.IsCancellationRequested)
							// If cancellation has not been requested, the reason of this exception must be timeout.
							// This is for response header only.
							throw new TimeoutException("Reading response header timed out!");

						throw;
					}
					catch (ObjectDisposedException)
					{
						if (cancellationToken.IsCancellationRequested)
							// If cancellation has been requested, the reason of this exception must be cancellation.
							// This is for response content only.
							throw new OperationCanceledException();

						throw;
					}
					catch (IOException ie)
					{
						var inner = ie.InnerException;
						if ((inner != null) && (inner.GetType() == typeof(WebException)) &&
							(((WebException)inner).Status == WebExceptionStatus.ConnectionClosed) &&
							cancellationToken.IsCancellationRequested)
							// If cancellation has been requested during downloading, this exception may be thrown.
							throw new OperationCanceledException();

						throw;
					}
					catch (HttpRequestException hre)
					{
						var inner = hre.InnerException;
						if ((inner != null) && (inner.GetType() == typeof(WebException)))
							// If unable to connect to FlashAir card, this exception will be thrown.
							// The Status may vary, such as WebExceptionStatus.NameResolutionFailure,
							// WebExceptionStatus.ConnectFailure.
							throw new RemoteConnectionUnableException(((WebException)hre.InnerException).Status);

						throw;
					}
				}
				catch (RemoteConnectionUnableException)
				{
					if (retryCount >= _retryCountMax)
						throw;
				}
				catch (Exception ex)
				{
					Debug.WriteLine("Failed to download byte array. {0}", ex);
					throw;
				}

				// Wait interval before retry.
				if (TimeSpan.Zero < _retryInterval)
					await Task.Delay(_retryInterval, cancellationToken);
			}
		}
Пример #29
0
 private static async Task <byte[]> DownloadBytesAsync(HttpClient client, string path, int size, CardInfo card, CancellationToken cancellationToken)
 {
     return(await DownloadBytesAsync(client, path, size, null, card, cancellationToken));
 }
Пример #30
0
        /// <summary>
        /// Get a list of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="client">HttpClient</param>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="token">CancellationToken</param>
        /// <param name="card">FlashAir card information</param>
        /// <remarks>This method is part of parent method.</remarks>
        private static async Task <List <FileItemViewModel> > GetFileListEachAsync(HttpClient client, string remoteDirectoryPath, CancellationToken token, CardInfo card)
        {
            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

            var items = await DownloadStringAsync(client, remotePath, token, card);

            return(items.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                   .Select(item => new FileItemViewModel(item, remoteDirectoryPath))
                   .Where(x => x.IsImported)
                   .ToList());
        }
Пример #31
0
 internal Task <BitmapSource> GetThumbnailAsync(string remoteFilePath, CardInfo card, CancellationToken cancellationToken) =>
 GetThumbnailAsync(_client, remoteFilePath, card, cancellationToken);
Пример #32
0
 /// <summary>
 /// Gets a list of all files recursively from root folder of FlashAir card.
 /// </summary>
 /// <param name="client">HttpClient</param>
 /// <param name="card">FlashAir card information</param>
 /// <param name="cancellationToken">CancellationToken</param>
 /// <returns>File list</returns>
 internal static async Task <IEnumerable <IFileItem> > GetFileListRootAsync(HttpClient client, CardInfo card, CancellationToken cancellationToken)
 {
     try
     {
         return(await GetFileListAllAsync(client, Settings.Current.RemoteDescendant, card, cancellationToken).ConfigureAwait(false));
     }
     catch
     {
         Debug.WriteLine("Failed to get all file list.");
         throw;
     }
 }
Пример #33
0
 private static Task <byte[]> DownloadBytesAsync(HttpClient client, string path, CardInfo card, CancellationToken cancellationToken)
 {
     return(DownloadBytesAsync(client, path, 0, null, card, cancellationToken));
 }
Пример #34
0
        /// <summary>
        /// Get a list of files in a specified directory in FlashAir card.
        /// </summary>
        /// <param name="remoteDirectoryPath">Remote directory path</param>
        /// <param name="token">CancellationToken</param>
        /// <param name="card">FlashAir card information</param>
        /// <remarks>This method is not actually used.</remarks>
        internal static async Task <List <FileItemViewModel> > GetFileListAsync(string remoteDirectoryPath, CancellationToken token, CardInfo card)
        {
            if (String.IsNullOrWhiteSpace(remoteDirectoryPath))
            {
                throw new ArgumentNullException("remoteDirectoryPath");
            }

            var remotePath = ComposeRemotePath(FileManagerCommand.GetFileList, remoteDirectoryPath);

            try
            {
                using (var client = new HttpClient()
                {
                    Timeout = timeoutLength
                })
                {
                    var items = await DownloadStringAsync(client, remotePath, token, card).ConfigureAwait(false);

                    return(items.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                           .Select(item => new FileItemViewModel(item, remoteDirectoryPath))
                           .Where(x => x.IsImported)
                           .ToList());
                }
            }
            catch
            {
                Debug.WriteLine("Failed to get file list.");
                throw;
            }
        }