I'm trying to generate a zip file of pdfs asynchronously to speed things up as follows:
var files = new Dictionary<string, byte[]>();
var fileTasks = new List<Task<Library.Models.Helpers.File>>();
foreach (var i in groups)
{
var task = Task.Run(async () =>
{
var fileName = $"{i.Key.Title.Replace('/', '-')} - Records.pdf";
ViewBag.GroupName= i.Key.Title;
var html = await this.RenderViewAsync("~/Views/Report/_UserRecordsReport.cshtml", i.ToList(), true);
return await _fileUtilityService.HtmlToPDF2(html, null, fileName);
});
fileTasks.Add(task);
}
var completedTaskFiles = await Task.WhenAll(fileTasks);
foreach(var item in completedTaskFiles)
{
files.Add($"{item.FileName}", item.FileResult);
}
return _fileUtilityService.GenerateZIP(files);
I'm generating all my html to pdf file tasks and waiting for them to be completed - then trying to synchronously loop through the completed tasks and add them to my dictionary for zipping but I keep getting the following error:
An item with the same key has already been added
There is no duplicate key in the list of items being added.
EDIT - so the current idea is that because its a scoped service, thats why i'm running into thread issues (attached the file utility service for information)
public class FileUtilityService : IFileUtilityService
{
private readonly IHttpClientFactory _clientFactory;
public FileUtilityService(IHttpClientFactory clientFactory)
{
public async Task<byte[]> HtmlToPDF(string html = null, string url = null)
{
try
{
byte[] res = null;
if (html is null && url != null)
{
var client = _clientFactory.CreateClient();
var requestResp = await client.GetAsync(url);
using var sr = new StreamReader(await requestResp.Content.ReadAsStreamAsync());
html = HttpUtility.HtmlDecode(await sr.ReadToEndAsync());
}
using(var ms = new MemoryStream())
{
HtmlConverter.ConvertToPdf(html, ms);
res = ms.ToArray();
}
return res;
}catch(Exception ex)
{
throw ex;
}
}
public async Task<Library.Models.Helpers.File> HtmlToPDF(string html = null, string url = null, string fileName = "")
{
return new Library.Models.Helpers.File() { FileName = fileName, FileResult = await HtmlToPDF(html, url) };
}
Related
I am trying to upload an image to cloudinary cloud. The file converts fine to memory stream but when I try to call upload method of cloudinary to upload the image, I get InvlalidOperationException. What I think is, there is something wrong with converting file to stream.See the image showing error
[HttpPost]
public async Task<IActionResult> AddPhotoForUser(int userId, [FromForm] AddPhotoDto addPhotoDto)
{
try
{
if (userId != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
{
return Unauthorized();
}
var userFromRepo = await _datingRepository.GetUser(userId);
var file = addPhotoDto.File;
var uploadResult = new ImageUploadResult();
if (file.Length > 0)
{
using (var stream = file.OpenReadStream())
{
var uploadParams = new ImageUploadParams()
{
File = new FileDescription(file.Name, stream),
Transformation = new Transformation()
.Width(500).Height(500).Crop("fill").Gravity("face")
};
uploadResult = _cloudinary.Upload(uploadParams);
}
}
addPhotoDto.Url = uploadResult.Url.ToString();
addPhotoDto.PublicId = uploadResult.PublicId;
var photo = _mapper.Map<Photo>(addPhotoDto);
if (!userFromRepo.Photos.Any(p => p.IsMain))
{
photo.IsMain = true;
}
userFromRepo.Photos.Add(photo);
if (await _datingRepository.SaveAll())
{
var photoToReturn = _mapper.Map<ReturnPhotoDto>(photo);
return CreatedAtRoute("GetPhoto", new { id = photo.Id }, photoToReturn);
}
return BadRequest("Could not add photo");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
Can you please share why do you use open stream? You can try:
var imageuploadParams = new ImageUploadParams () {
File = new FileDescription (#"https://res.cloudinary.com/demo/image/upload/v1561532539/sample.jpg"),
PublicId = "myimage",
Transformation = new Transformation().Width(500).Height(500).Crop("fill").Gravity("face")
};
var ImageuploadResult = cloudinary.Upload (imageuploadParams);
I'm using C# .net core to read upload data from multipart post user sending multiple files.
How can I prevent use waiting infinite after read last file in
try
{
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(1000);
section = await multipartReader.ReadNextSectionAsync(cancellationTokenSource.Token);
}
catch (Exception ex)
{
throw;
}
Altough I've set cancelationToken for 1 second but it goes infinite, and won't go to
next line if I will send another request.
public static async Task<HttpRequest> FromHttpContextAsync(HttpContext httpContext)
{
bool multipart = false;
HttpRequest retVal = new HttpRequest(httpContext);
var sb = new StringBuilder();
var sr = new StreamReader(httpContext.Stream, Encoding.UTF8);
{
var line1 = await sr.ReadLineAsync();
sb.AppendLine(line1);
var Line1Parts = (line1).Split(' ');
retVal.Methode = Line1Parts[0].ToLower();
retVal.RawUrl = System.Net.WebUtility.UrlDecode(Line1Parts[1]).Replace("&", "&");
var urlPart = retVal.RawUrl.Split('?');
retVal.Url = urlPart[0];
if (urlPart.Length > 1)
{
foreach (var part in urlPart[1].Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
{
var tmp = part.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
try
{
retVal.QueryStrings.Add(tmp[0], tmp[1]);
}
catch (Exception ex)
{
}
}
}
string line = await sr.ReadLineAsync();
sb.AppendLine(line);
int contentLength = 0;
while (!string.IsNullOrEmpty(line))
{
var tmp = line.Split(':');
var key = tmp[0].Trim().ToLower();
retVal.Header.Add(new KeyValuePair<string, string>(tmp[0], tmp[1]));
switch (key)
{
case "cookie":
{
foreach (var part in tmp[1].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
var pares = part.Split('=');
if (pares.Length == 2)
{
retVal.Cookies.Add(new KeyValuePair<string, string>(pares[0], pares[1]));
}
}
break;
}
case "content-length":
{
contentLength = int.Parse(tmp[1]);
break;
}
}
line = await sr.ReadLineAsync();
sb.AppendLine(line);
}
if (sb.ToString().Contains("Content-Type: multipart/form-data"))
{
string boundary = FindBoundary(sb.ToString());
MultipartReader multipartReader = new MultipartReader(boundary, httpContext.Stream);
var section = await multipartReader.ReadNextSectionAsync();
while (section != null)
{
// process each image
const int chunkSize = 1024;
var buffer = new byte[chunkSize];
var bytesRead = 0;
var fileName = GetFileName(section.ContentDisposition);
using (var stream = new MemoryStream())
{
do
{
try
{
bytesRead = await section.Body.ReadAsync(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.Write(ex);
}
stream.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
retVal.Files.Add(new Tuple<string, string, byte[]>("", fileName, stream.ToArray()));
}
try
{
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(1000);
section = await multipartReader.ReadNextSectionAsync(cancellationTokenSource.Token);
}
catch (Exception ex)
{
throw;
}
}
foreach (var file in retVal.Files)
{
File.WriteAllBytes("d:\\" + file.Item2, file.Item3);
}
}
HttpContext is inline class of this project and this is the source of HttpContext :
public class HttpContext
{
public HttpRequest Request { get; private set; }
public HttpResponse Response { get; private set; }
public Stream Stream { private set; get; }
private HttpContext(Stream networkStream)
{
Stream = networkStream;
}
public async static Task<HttpContext> FromHttpContextAsync(Stream networkStream)
{
var retVal = new HttpContext(networkStream);
retVal.Request = await HttpRequest.FromHttpContextAsync(retVal);
retVal.Response = HttpResponse.FromHttpContext(retVal);
return retVal;
}
}
While the lack of details and context makes trying to reproduce this issue really hard, I suspect the problem here is due to the fact NetworkStreams (used, under the covers, by your MultipartReader instance) do not yet fully support CancellationTokens. In fact, almost every Socket-related operation on .NET Core just checks for the eventually passed CancellationToken upfront - which is useless, in my opinion.
The good news is that the .NET Core team is actively working on this and I believe the issue will be completely solved in .NET Core 3.0:
https://github.com/dotnet/corefx/issues/24430
As a temporary ugly workaround, you can change your code to wait for both a fabricated delay task and your call to ReadNextSectionAsync(), assuming you don't want to re-used that stalled socket / NetworkStream after that:
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
var readNextSectionTask = multipartReader.ReadNextSectionAsync().ConfigureAwait(false);
if (await Task.WhenAny(timeoutTask, readNextSectionTask).ConfigureAwait(false) == timeoutTask)
{
// TODO: Handle the timeout
}
else
{
section = await readNextSectionTask;
}
Additionally, the fact you are not configuring your awaitable may act as a possible deadlock source (it is unclear to me whether you are running this code on ASP.NET Core itself or on some other synchronization context provider). To exclude this possibility, I would suggest to call ConfigureAwait(false) right after your await calls, as you can see on my previous code block.
I am working on Windows 10 UWP app and my requirement is to upload 5 images on the server with unique value. So, I have used System.Threading.Tasks.Task.Factory.StartNew().Now, when I checked while debugging, I found that randomly sometimes for 2 images, it sends same unique key. Can someone suggest is it better to use System.Threading.Tasks.Task.Factory.StartNew()?
All the images are sent using a web service. My sample code for this is following
WebServiceUtility serviceUtility = new WebServiceUtility();
List<System.Threading.Tasks.Task> tasks = new List<System.Threading.Tasks.Task>();
var cancelSource = new CancellationTokenSource();
cancellationToken = cancelSource.Token;
System.Threading.Tasks.Task currentTask = null;
List<System.Threading.Tasks.Task> uploadTasks = new List<System.Threading.Tasks.Task>();
List<string> uploadedImageIdList = new List<string>();
foreach (var image in _imageCollection)
{
if (!cancellationToken.IsCancellationRequested)
{
currentTask = await System.Threading.Tasks.Task.Factory.StartNew(async () =>
{
string imageName = string.Empty;
string imagePath = string.Empty;
if (image.IsEvidenceImage)
{
imageName = image.EvidencePath.Split('\\')[1];
imagePath = image.EvidencePath;
}
else
{
imageName = image.EvidencePath.Split('#')[1].Split('\\')[1];
imagePath = image.EvidencePath.Split('#')[1];
}
byte[] _imageAsByteArray = await GetEvidenceFromIsoStore(imagePath);
if (null != _imageAsByteArray && _imageAsByteArray.Length > 0)
{
IRestResponse response = await serviceUtility.UploadImage
(_imageAsByteArray, imageName,
new RequestDataGenerator().generateRequestDataForMediaUpload(
(null != _imageItem.I_IS_PRIMARY && "1".Equals(_imageItem.I_IS_PRIMARY) ? "1" : "0"),
evidenceName
));
if (response != null && response.RawBytes.Length > 0)
{
var successMessage = MCSExtensions.CheckWebserviceResponseCode(response.StatusCode);
if (successMessage.Equals(Constants.STATUS_CODE_SUCCESS))
{
byte[] decryptedevidenceresponse = WebserviceED.finaldecryptedresponse(response.RawBytes);
string responseString = Encoding.UTF8.GetString(decryptedevidenceresponse, 0, decryptedevidenceresponse.Length);
JObject reponseObject = JObject.Parse(responseString);
//Debug.WriteLine("Evidence Upload Response : " + Environment.NewLine);
uploadedimageIdList.Add(reponseObject["P_RET_ID"].ToString());
try
{
if (image.IsEvidenceImage)
{
if (await FileExists(image.EvidencePath))
{
StorageFile file = await localFolder.GetFileAsync(image.EvidencePath);
await file.DeleteAsync();
}
}
else
{
string[] evidenceMedia = image.EvidencePath.Split('#');
foreach (string evidenceItem in evidenceMedia)
{
if (await FileExists(evidenceItem))
{
StorageFile file = await localFolder.GetFileAsync(evidenceItem);
await file.DeleteAsync();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
else
{
UserMessageUtil.ShowMessage(successMessage);
}
}
}
}, cancellationToken);
uploadTasks.Add(currentTask);
}
}
await System.Threading.Tasks.Task.WhenAll(uploadTasks.ToArray());
Just make it a separate method:
...
foreach (var image in _imageCollection)
{
if (!cancellationToken.IsCancellationRequested)
{
currentTask = UploadAsync(...);
uploadTasks.Add(currentTask);
}
}
await Task.WhenAll(uploadTasks);
async Task UploadAsync(...)
{
string imageName = string.Empty;
string imagePath = string.Empty;
...
}
Or, a bit more simply at the call site:
...
var uploadTasks = _imageCollection.Select(x => UploadAsync(...));
await Task.WhenAll(uploadTasks);
I have the following function that uploads files to Drobox and returns shared links to these files.
private async Task<string> Upload(DropboxClient dbx, string localPath, string remotePath)
{
const int ChunkSize = 4096 * 1024;
using (var fileStream = File.Open(localPath, FileMode.Open))
{
if (fileStream.Length <= ChunkSize)
{
WriteMode mode = new WriteMode();
FileMetadata fileMetadata = await dbx.Files.UploadAsync(remotePath, body: fileStream, mode: mode.AsAdd, autorename: true);
//set the expiry date
var existingDoc = await dbx.Files.GetMetadataAsync(remotePath);
if (existingDoc.IsFile)
{
var sharedLink = dbx.Sharing.ListSharedLinksAsync(remotePath);
var settings = new ListSharedLinksArg(remotePath);
ListSharedLinksResult listSharedLinksResult = await dbx.Sharing.ListSharedLinksAsync(remotePath);
if (listSharedLinksResult.Links.Count > 0)
{
return listSharedLinksResult.Links[0].Url;
}
else
{
var settings2 = new SharedLinkSettings(expires: DateTime.Today.AddDays(7));
SharedLinkMetadata sharedLinkMetadata = await dbx.Sharing.CreateSharedLinkWithSettingsAsync(remotePath, settings2);
return sharedLinkMetadata.Url;
}
}
else
{
var settings = new SharedLinkSettings(expires: DateTime.Today.AddDays(7));
SharedLinkMetadata sharedLinkMetadata = await dbx.Sharing.CreateSharedLinkWithSettingsAsync(fileMetadata.PathLower, settings);
return sharedLinkMetadata.Url;
}
}
else
{
await this.ChunkUpload(dbx, remotePath, fileStream, ChunkSize);
}
return "error";
}
}
But it's not working properly, when it gets to the ListSharedLinksAsync function, it stops working witout throwing any error.
I notices that the files that I try to upload are not accessible after it crashes, I get a "used by another proccess error"...
What am I doing wrong?
It looks like you are missing an await on this row, thus causing a deadlock?
var sharedLink = dbx.Sharing.ListSharedLinksAsync(remotePath);
Should be
var sharedLink = await dbx.Sharing.ListSharedLinksAsync(remotePath);
I am working on bot technology, in one of my projet i wrote below lines of code
private async void DeliveryProgressReport(IDialogContext context, Activity message)
{
MessagesController.progressdetails = SQLDatabaseService.getinprogressdetails();
var progress = MessagesController.progressdetails;
if (progress.Count > 0)
{
try
{
Activity replyToConversation = message.CreateReply("**In Progress Report Details**");
replyToConversation.Recipient = message.From;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();
Dictionary<string, string> progresslist = new Dictionary<string, string>();
foreach (var progressreport in progress)
{
//Invoke the machine learning model for predicting the delivery status of delivery person
//var deliveryStatus= await InvokeRequestResponseServiceOfDeliveryPersonPredictionExp1();
//await Task.Delay(TimeSpan.FromSeconds(5));
var deliveryStatus = await InvokeRequestResponseServiceOfDeliveryPersonPredictionExp(progress[0].Name, progress[0].Mobile_Number);
progresslist.Add(progressreport.Name, progressreport.Mobile_Number);
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: progressreport.Photo_Url));
ThumbnailCard tlcard = new ThumbnailCard()
{
Title = "Name:" + progressreport.Name,
Subtitle = "Call:" + progressreport.Mobile_Number,
Images = cardImages,
Text = "Staus by Using Machine Learning Prediction:" + deliveryStatus
};
Attachment plAttachment = tlcard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
}
replyToConversation.AttachmentLayout = AttachmentLayoutTypes.List;
await context.PostAsync(replyToConversation);
} catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
else
{
Activity replyToConversation = message.CreateReply("**There are no in progress deliveries are found**");
await context.PostAsync(replyToConversation);
}
}
private async Task<string> InvokeRequestResponseServiceOfDeliveryPersonPredictionExp(string name, string mobile_Number)
{
string status = "";
//Func<Stream, Task> copyStreamAsync = async stream =>
//{
//await Task.Factory.StartNew(async () =>
//{
//using (stream)
//using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
//{
// await sourceStream.CopyToAsync(stream);
//}
//var client = new HttpClient();
using (var client = new HttpClient())
{
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"input1",
new StringTable()
{
ColumnNames = new string[] {"Id", "Name", "Mobile_Number", "CourierCompany_Name", "Status", "EmailId"},
Values = new string[,] { { "", name, mobile_Number, "", "","" }, { "", name, mobile_Number, "", "", "" }, }
}
},
},
GlobalParameters = new Dictionary<string, string>()
{
}
};
const string apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx=="; // Replace this with the API key for the web service
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri("My Request URL");
// WARNING: The 'await' statement below can result in a deadlock if you are calling this code from the UI thread of an ASP.Net application.
// One way to address this would be to call ConfigureAwait(false) so that the execution does not attempt to resume on the original context.
// For instance, replace code such as:
// result = await DoSomeTask()
// with the following:
// result = await DoSomeTask().ConfigureAwait(false)
//var status = await PostRequest(scoreRequest,client).ConfigureAwait(false);
HttpResponseMessage response = await client.PostAsJsonAsync("", scoreRequest);//.ConfigureAwait(false);
string correctLocation = "";
string wrongLocation = "";
string notReached = "";
string personMismatch = "";
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
var results = JsonConvert.DeserializeObject<RootObject>(result);
foreach (var value in results.Results.output1.value.Values)
{
status = value[8].ToString();
correctLocation = value[4].ToString();
notReached = value[5].ToString();
personMismatch = value[6].ToString();
wrongLocation = value[7].ToString();
}
Debug.WriteLine("Result: {0}", result);
return status;
}
else
{
Debug.WriteLine(string.Format("The request failed with status code: {0}", response.StatusCode));
// Print the headers - they include the requert ID and the timestamp, which are useful for debugging the failure
Debug.WriteLine(response.Headers.ToString());
string responseContent = await response.Content.ReadAsStringAsync();
Debug.WriteLine(responseContent);
return status;
}
};
// return status;
}
After executing this below line I got the exception like asynchronous module or handler completed while an asynchronous operation was still pending
await context.PostAsync(replyToConversation);
Before posting this question I had followed through this below links, but I didn't resolve it.
Async Void, ASP.Net, and Count of Outstanding Operations
Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending
Please tell how to resolve this exception.
-Pradeep
Finally, I resolved the above exception when I am return Task instead of void in DeliveryProgressReport method. and also where ever I was called the await DeliveryProgressReport() method there also I return Task instead of void.
-Pradeep