multiple files download using httpclient - c#

I am working on xamarin.forms application. I want to download multiple files simultaneously using HttpClient. If there is multiple files then I am getting : System.IO.IOException:Sharing violation on path . Is there anything that has to be improved ?
Here is my code for downloading files :
public async Task DownloadFileAsync(string sourceUrl, string filePathWhereToSave, CancellationTokenSource cts)
{
Exception error = null;
bool isCancelled = false;
try
{
if (!downloadingTasks.ContainsKey(sourceUrl))
downloadingTasks.Add(sourceUrl, cts);
var token = cts.Token;
var response = await _client.GetAsync(sourceUrl, HttpCompletionOption.ResponseHeadersRead, token);
response.EnsureSuccessStatusCode();
string fileName = filePathWhereToSave.Substring(filePathWhereToSave.LastIndexOf('/'));
string directory = filePathWhereToSave.Substring(0, filePathWhereToSave.LastIndexOf('/'));
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
var totalData = response.Content.Headers.ContentLength.GetValueOrDefault(-1L);
var canSendProgress = totalData != -1L;
await Task.Run(async() =>
{
using (var fileStream = OpenStream(filePathWhereToSave))
{
using (var stream = await response.Content.ReadAsStreamAsync())
{
var totalRead = 0L;
var buffer = new byte[bufferSize];
var isMoreDataToRead = true;
do
{
var read = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (read == 0)
isMoreDataToRead = false;
else
{
await fileStream.WriteAsync(buffer, 0, read);
totalRead += read;
if (canSendProgress)
{
//var progress = ((totalRead * 1d) / (totalData * 1d) * 100);
MessagingCenter.Send<DownloadFileProgressChangedMessage>(new DownloadFileProgressChangedMessage(sourceUrl, totalRead, totalData, 0), MessageNameConstants.DownloadFileProgressChangedMessage);
}
}
} while (isMoreDataToRead);
}
}
});
}
catch (OperationCanceledException ex)
{
isCancelled = true;
}
catch (Exception e)
{
error = e;
System.Diagnostics.Debug.WriteLine(e.ToString());
}
finally
{
MessagingCenter.Send<DownloadCompletedMessage>(new DownloadCompletedMessage(sourceUrl, filePathWhereToSave, error, isCancelled), MessageNameConstants.DownloadCompletedMessage);
if (downloadingTasks.ContainsKey(sourceUrl))
downloadingTasks.Remove(sourceUrl);
}
}

This may be happening because the file is being locked by the reading stream, so the writing stream can't be created and you get the exception.
To avoid it you could enable read/write access with the class FileStream .
FileStream fileStream = new FileStream(filePathWhereToSave,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.None);
Or use StreamWriter
using (var writer = new StreamWriter(filePathWhereToSave))
{
// do work here.
}
BTW , what is OpenStream ? I can't find it in any assembly, is it included in a third-party library ?
Refer to
https://stackoverflow.com/a/23779697/8187800
https://stackoverflow.com/a/11541330/8187800

Related

Why when using httpclient it's not downloading multiple files from a list?

async Task DownloadFile()
{
// for the sake of the example lets add a client definition here
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0 Chrome");
var docUrl = "https://speed.hetzner.de/100MB.bin";
//var filePath = #"d:\Test\100MB.bin";
// Setup your progress reporter
var progress = new Progress<float>();
progress.ProgressChanged += Progress_ProgressChanged;
for (int i = 0; i < videosLinks.Count; i++)
{
var filePath = #"d:\Test\" + i + ".mp4";
try
{
using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
await client.DownloadDataAsync(videosLinks[i], file, progress);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
void Progress_ProgressChanged(object sender, float progress)
{
// Do something with your progress
progressBar1.Value = (int)progress;
}
private async void button1_Click(object sender, EventArgs e)
{
await DownloadFile();
}
when i used only a single file download :
"https://speed.hetzner.de/100MB.bin";
and instead the for loop just a single line :
using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
await client.DownloadDataAsync(docUrl, file, progress);
it was working fine and downloaded the big file with reporting to the progressBar.
but when i'm trying to download the files in the videosLinks List with the for loop it's just saving small files on the hard disk all the files same size 4.40KB and the downloaded files(mp4) should be in MB's it's just not downloading them.
i added a break point on the catch but it's never get there.
it's getting to the line :
using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
await client.DownloadDataAsync(videosLinks[i], file, progress);
not giving any errors or exceptions but not downloading the files.
i tried to add headers but it didn't change anything.
this is the class HttpClientProgressExtensions code :
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace HttpClientProgress
{
public static class HttpClientProgressExtensions
{
public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress<float> progress = null, CancellationToken cancellationToken = default(CancellationToken))
{
using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead))
{
var contentLength = response.Content.Headers.ContentLength;
using (var download = await response.Content.ReadAsStreamAsync())
{
// no progress... no contentLength... very sad
if (progress is null || !contentLength.HasValue)
{
await download.CopyToAsync(destination);
return;
}
// Such progress and contentLength much reporting Wow!
var progressWrapper = new Progress<long>(totalBytes => progress.Report(GetProgressPercentage(totalBytes, contentLength.Value)));
await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken);
}
}
float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f;
}
static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default(CancellationToken))
{
if (bufferSize < 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize));
if (source is null)
throw new ArgumentNullException(nameof(source));
if (!source.CanRead)
throw new InvalidOperationException($"'{nameof(source)}' is not readable.");
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (!destination.CanWrite)
throw new InvalidOperationException($"'{nameof(destination)}' is not writable.");
var buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead;
progress?.Report(totalBytesRead);
}
}
}
}

My memory stream does not write to the content of the httpResponse a ZIP file

I am trying to write the stream resulting from compressing several files in a ZIP but I can't. The ZIP file does, but when I want to write the resulting stream, the copyto method does nothing and my http request never ends.
I don't know why my logic doesn't work, I hope you can help me please.
public async Task<HttpResponseMessage> downloadFile3(string filePath, System.Threading.CancellationToken token)
{
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new PushStreamContent(async (streamout, context, transportContext) =>
{
try
{
using (var ms = new MemoryStream())
{
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
var entry = zipArchive.CreateEntry(filePath);
using (var fileStream = File.OpenRead(filePath))
{
using (var entryStream = entry.Open())
{
await fileStream.CopyToAsync(entryStream);
}
}
}
ms.Position = 0;
ms.CopyTo(streamout); //THIS LINE DOESN'T WORK
}
}
catch (Exception ex)
{
}
finally
{
streamout.Close();
}
}, "application/zip"),
};
response.Content.Headers.ContentLength = new FileInfo(filePath).Length;
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
Size = new FileInfo(filePath).Length,
FileName = Path.GetFileName(filePath)
};
return response;
}

How to save audio from using Windows.Media.SpeechSynthesis?

I´m using Windows.Media.SpeechSynthesis for TTS and playing an audio signal works fine in my WPF application. I have wanted to save an audio signal but if I call StorageFolder I get an error: HRESULT: 0x80073D54 - The process has no package identity. How to fix it? The whole code is below. I would also like to know what rights apply to the use of an audio file from Win 10 TTS? I didn't find it anywhere - but I wasn't looking for that much...
private async void Talk(string text)
{
var stream = await speechSynthesizer.SynthesizeTextToStreamAsync(text);
StorageFolder localfolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await localfolder.CreateFileAsync("sample.wav", CreationCollisionOption.ReplaceExisting);
using (var reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
IBuffer buffer = reader.ReadBuffer((uint)stream.Size);
await FileIO.WriteBufferAsync(sampleFile, buffer);
}
}
Finally, I have found a solution, how to save a stream from tts into *.wav or to *.mp3 by Naudio. I don´t know if it is clear but it is functional:
public bool ByteArrayToFile(string fileName, byte[] byteArray)
{
try
{
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(byteArray, 0, byteArray.Length);
return true;
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in process: {0}", ex);
return false;
}
}
private async Task SaveAudio(string text)
{
var stream = await speechSynthesizer.SynthesizeTextToStreamAsync(text);
using (var reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
IBuffer buffer = reader.ReadBuffer((uint)stream.Size);
DataReader dataReader = DataReader.FromBuffer(buffer);
byte[] bytes = new byte[buffer.Length];
dataReader.ReadBytes(bytes);
//ByteArrayToFile("sample.wav", bytes);
ConvertWavStreamToMp3File(bytes, TB_File.Text);
MessageBox.Show("Audio was saved to file: " + TB_File.Text, "Info");
}
}
public static void ConvertWavStreamToMp3File(byte[] wavFile, string savetofilename)
{
using (var retMs = new MemoryStream())
using (var ms = new MemoryStream(wavFile))
using (var rdr = new WaveFileReader(ms))
using (var wtr = new LameMP3FileWriter(savetofilename, rdr.WaveFormat, LAMEPreset.VBR_90))
{
rdr.CopyTo(wtr);
}
}

Upload zipped file to Dropbox using C#

I am trying to upload a zipped file to Dropbox using access token. Below code works for unzipped file:
private static async Task FileUploadToDropbox(string filePath, string fileName, byte[] fileContent)
{
var client = new DropboxClient("Access Token");
const int chunkSize = 1024;
using (var stream = new MemoryStream(fileContent))
{
int numChunks = (int)Math.Ceiling((double)stream.Length / chunkSize);
byte[] buffer = new byte[chunkSize];
string sessionId = null;
for (var idx = 0; idx < numChunks; idx++)
{
var byteRead = stream.Read(buffer, 0, chunkSize);
using (MemoryStream memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
else
{
UploadSessionCursor cursor = new UploadSessionCursor(sessionId, (ulong)(chunkSize * idx));
if (idx == numChunks - 1)
{
await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(filePath + "/" + fileName), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
}
}
}
}
But when I try to upload a zipped file using this code, it uploads an empty zipped file to Dropbox. I am reading the zipped file as a byte array and passing it to the above method. Although the file size remains the same, when i download the file and try to extract it, it says that the zipped file is empty.
private static async Task FileUploadToDropbox(string filePath, string fileName, string fileSource)
{
using (var dbx = new DropboxClient("access Token"))
using (var fs = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
var updated = await dbx.Files.UploadAsync(
(filePath + "/" + fileName), WriteMode.Overwrite.Instance, body: fs);
}
}
Above method worked for me.
Please try this:
/// <summary>
/// Function to import local file to dropbox.
/// </summary>
public static async Task<bool> WriteFileToDropBox()
{
try
{
//Connecting with dropbox.
var file = "File path at dropbox";
using (var dbx = new DropboxClient("Access Token"))
using (var fs = new FileStream("Path of file to be uploaded.")
{
var updated = await dbx.Files.UploadAsync(file, WriteMode.Add.Instance, body: fs);
}
return true;
}
catch (Exception err)
{
MessageBox.Show(err.Message);
return false;
}
}

System.ObjectDisposedException UWP

i've an issue in my application. I'm trying to send an image into FTP server. I'm able to connect with server, authenticate, and other. But when i try to send file, if the file is big (usually bigger than 40-50kb) i got System.ObjectDisposedException.
This is the code that i use to send:
public async Task <byte[]> GetResultingBuffer(IRandomAccessStreamWithContentType readStream, IBuffer buffer)
{
var resultingBuffer = new byte[0];
while (true)
{
IBuffer readBuffer = await readStream.ReadAsync(buffer, 1024, InputStreamOptions.Partial);
if (readBuffer.Length == 0) break;
resultingBuffer = resultingBuffer.Concat(readBuffer.ToArray()).ToArray();
}
return resultingBuffer;
}
public async Task UploadFileAsync(StorageFile file, string destination)
{
using (var stream = await OpenWriteAsync(destination))
{
//
// A more efficient way, maybe a DataReader can be used here
using (var readStream = await file.OpenReadAsync())
{
var buffer = new byte[1024].AsBuffer();
var resultingBuffer = new byte[0];
resultingBuffer = await GetResultingBuffer(readStream, buffer);
await stream.WriteAsync(resultingBuffer.AsBuffer());
await stream.FlushAsync();
}
}
}
I tried to edit it, before editing GetResultingBuffer was not a task, but a cycle inside UploadFileAsnc. How can i prevent to dispose the buffer? Is there another solution? Thanks!
I solved it in this way
using (var readStream = await file.OpenReadAsync())
{
var buffer = new byte[3000000].AsBuffer();
//var resultingBuffer = new byte[10000000];
Debug.Write("-------");
//while (true)
//{
IBuffer readBuffer = await readStream.ReadAsync(buffer, 3000000, InputStreamOptions.Partial);
//if (readBuffer.Length == 0) break;
//resultingBuffer = resultingBuffer.Concat(readBuffer.ToArray()).ToArray();
//}
// await stream.WriteAsync(resultingBuffer.AsBuffer());
var resultingBuffer = new byte[readBuffer.Length];
readBuffer.CopyTo(resultingBuffer);
await stream.WriteAsync(resultingBuffer.AsBuffer());
}
Debug.Write("-------");
await stream.FlushAsync();

Categories

Resources