Win 10 zip upload reorders blocks of data - c#

Our system has been used to upload millions of files over several years. The clients use the following code to send an authentication token and zip file to our WEB API on Windows Server 2008 R2. On our Windows 7 devices, the system works great. As we are attempting to move to Windows 10 devices, we have suddenly encountered an issue where the received file has blocks of data in a different order than the source file. The problem only occurs about half of the time, which makes it very difficult to track down.
client code (.NET 4.5)
private static void UploadFile(string srcFile, string username, string password)
{
if (File.Exists(srcFile))
{
ConnectionUtilities connUtil = new ConnectionUtilities();
string authToken = connUtil.GetAuthToken(username, password);
using (HttpContent authContent = new StringContent(authToken))
{
using (HttpContent fileStreamContent = new ByteArrayContent(File.ReadAllBytes(srcFile)))
{
FileInfo fi = new FileInfo(srcFile);
using (HttpClient client = new HttpClient())
using (MultipartFormDataContent formData = new MultipartFormDataContent())
{
client.DefaultRequestHeaders.ExpectContinue = false;
formData.Add(authContent, "auth");
formData.Add(fileStreamContent, "data", fi.Name);
var response = client.PostAsync(ConfigItems.hostName + "UploadData", formData).Result;
if (response.IsSuccessStatusCode)
{
File.Delete(srcFile);
}
}
}
}
}
}
WEB API code (.NET 4.5.2)
public async Task<HttpResponseMessage> PostUploadData()
{
if (Request.Content.IsMimeMultipartContent())
{
MultipartFormDataStreamProvider streamProvider =
MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/app_data"));
await Request.Content.ReadAsMultipartAsync(streamProvider);
string auth = streamProvider.FormData["auth"];
if (auth != null)
{
auth = HttpUtility.UrlDecode(auth);
}
if (Util.IsValidUsernameAndPassword(auth))
{
string username = Util.GetUsername(auth);
foreach (var file in streamProvider.FileData)
{
DirectoryInfo di = new DirectoryInfo(ConfigurationManager.AppSettings["DataRoot"]);
di = di.CreateSubdirectory(username);
string contentFileName = file.Headers.ContentDisposition.FileName;
di = di.CreateSubdirectory("storage");
FileInfo fi = new FileInfo(file.LocalFileName);
string destFileName = Path.Combine(di.FullName, contentFileName);
File.Move(fi.FullName, destFileName);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
}
The problem initially manifests as a zipped file that can't open in Windows. Only by doing a hexadecimal compare did we determine that the file was all there, just not in the same order as the original.
Any thoughts on what might be causing the blocks of data to be reordered?
P.S. I know the HttpClient is not being used as effectively as possible.

After some long and tedious testing (Yay, scientific method) we determined that our web content filter software was causing the issue.

Related

How do I render a pdf in the browser generated from DynamicPDF Cloud API?

Using DynamicPDF's Cloud API, instead of generating a pdf back to the local file system, I would like it to directly open in another browser tab to be available for printing immediately. How do I accomplish that?
The method I am using (.NET Core 6 / Blazor) is below:
public async Task CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
File.WriteAllBytes(basePath + "Manifest_" + manifestBranch + ".pdf", pdfResponse.Content);
}
else
{
Console.WriteLine(pdfResponse.ErrorJson);
}
}
Reread article on https://learn.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0
#page "/file-download-1"
#using System.IO
#inject IJSRuntime JS
<h1> File Download Example</h1>
<button #onclick = "DownloadFileFromStream" >
Download File From Stream
</button>
#code {
private Stream CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
return new MemoryStream(pdfResponse.Content);
}
else
{
throw new Exception("");
}
}
private async Task DownloadFileFromStream()
{
var fileStream = CallDynPDFCloudAPI();
var fileName = "file.pdf";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
You won't be able to access the PDF content for this request from another browser tab. I'd recommend opening the new tab before making the call and then streaming it there. If you're using an 'a href' link, you can accomplish this by setting the 'target="_blank"' property of the 'a href'. If this is a form submission, you can set the 'target="_blank"' property of the 'form'.
The other option would be to store the PDF somewhere temporarily (as a file, in a DB or in BLOB storage) then stream it to the other tab once it's opened.
Ive impletented #Mihal's answer, with modified code suggested by #DynamicPDF to achieve the result I was looking for. My two goals were:
Not clutter the client device with excessive dowloaded files
Not needing to save the file in Server or DB
Javascript:
<script>
window.downloadFileFromStream = async (fileName,
contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
//--Opens PDF file in new Tab
fetch(url)
.then(response => response.blob())
.then(data => window.open(URL.createObjectURL(data), '_blank'))
//--Downloads file to Browser (uncomment if desired)
//const anchorElement = document.createElement('a');
//anchorElement.href = url;
//anchorElement.download = fileName ?? 'Manifest';
//anchorElement.click();
//anchorElement.remove();
//URL.revokeObjectURL(url);
}
</script>
*NOTE! My application is purely internal-facing to our organization. Our Windows client machines and browsers are managed by Group Policy. I have not yet tested this on Mac / Safari clients yet.

Send file from Android Kotlin to C# using POST

We have two applications: A C# REST-API, and a Kotlin Android application, we are using Google Platform Cloud Bucket to host the images.
A picture will be uploaded on the Android application, but the C# REST-API needs to upload it to the Google Cloud Platform.
This is the working C# code to upload a file to the Google Cloud Buckets:
[HttpPost]
[Route("upload")]
public IActionResult Upload()
{
var storageClient = StorageClient.Create(google_credentials);
string fileToUpload ="/Users/niel/Downloads/new_cat.jpg";
using (var fileStream = new FileStream(fileToUpload, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
storageClient.UploadObject("test_storage_fotos", "new_cat", "image/jpeg", fileStream);
}
Console.WriteLine("uploaded the file successfully");
return Ok();
}
Now I need to replace fileToUpload with the content from a POST-request. Is there a way to do this? Picture from Android app > C# API > Google Buckets? The link from the C# API to Google Buckets is already working.
Is there a way in Kotlin to somehow get the byte-string of an image, post it to my C# API who takes the content and puts it in a FileStream? I than can upload the FileStream using storageClient.UploadObject? Is this a possibility?
Thanks!
Yes, you can definitely do this. Just send the file over to the server via http protocol with multipart/form-data content type.
In kotlin you can use ktor or any other http library to do that.
For ktor you'll need to add an implementation dependency
implementation "io.ktor:ktor-client-android:1.5.4"
And you might also need to add additional permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
Then you can send a file with this snippet. Notice that imageUri is a content uri, for file uri the code would be a bit different
private fun getFileName(resolver: ContentResolver, uri: Uri): String {
val returnCursor: Cursor = resolver.query(uri, null, null, null, null)!!
val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
val name: String = returnCursor.getString(nameIndex)
returnCursor.close()
return name
}
suspend fun postAndImage(imageUri: Uri, uploadEndPoint: String) {
val client = HttpClient(Android)
val cr = applicationContext.contentResolver
if(cr.getType(imageUri) == null) {
//process error
return
}
val stream = cr.openInputStream(imageUri)
if(stream == null) {
//process error
return
}
val response: HttpResponse = client.submitFormWithBinaryData(
url = uploadEndPoint,
formData = formData {
append("image", InputProvider { stream.asInput() }, Headers.build {
append(HttpHeaders.ContentType, cr.getType(imageUri)!!)
append(HttpHeaders.ContentDisposition, "filename=${getFileName(cr, imageUri)}")
})
}
)
stream.close()
//process response
}
And you'll need to modify you upload function slightly
[HttpPost]
[Route("upload")]
//the name of the argument must match the key that you pass in "append" function
public async Task<IActionResult> Post(IFormFile image)
{
var storageClient = StorageClient.Create(google_credentials);
using (var stream = image.OpenReadStream())
{
//it's also possible to get original file name from file name property
var fileName = Guid.NewGuid() + "." + Path.GetExtension(image.FileName);
//assuming bucket is already created
var storageObject = await storageClient
.UploadObjectAsync("test_storage_fotos", fileName, "image/jpeg", stream);
//save information about a storage object in database
}
return Ok();
}

HttpClient.GetByteArrayAsync(…) “deadlock” when there is no internet connection in .NET standard library calling from UWP

I am developing a UWP application for a document management system. I am trying to open documents from my application. When I click the open document, It is going to download the document and then open in the default application. But the problem is document is not downloaded if the internet is a disconnect in the middle of the process. It means when httpClient is already called. My code is as following
public async Task<DownloadFileDetail> DownloadFileAsync(int dmsFileId)
{
if (dmsFileId <= 0)
{
throw new ArgumentException("Invalid DMS File Id");
}
try
{
return await Task.Run(async () =>
{
DownloadFileDetail fileDetail = new DownloadFileDetail()
{
DocId = dmsFileId
};
string apiUrl = $"files/download/latest/{dmsFileId}";
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(BaseApiUrl);
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {SessionStore.Instance.AuthToken}");
var response = await httpClient.GetByteArrayAsync(apiUrl); --> gone deadlock
fileDetail.Content = response;
return fileDetail;
});
}
catch (Exception ex)
{
}
return new DownloadFileDetail()
{
DocId = dmsFileId
};
}
Download process called as UWP->.NET Standard Library (holds above code). It will be great if someone helps me to solve the problem.
Thanks
ss
Update:
The above code is working on my laptop and not working on any other laptop in dev environment
when there is no internet connection in .NET standar library calling from UWP
If the deadlock only occurs in no internet connection environment, you could check if internet is available before sending http request. Please check this NetworkHelper.
if (NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
// sending the request.
}
First, remove the Task.Run(async () => ...) call:
try
{
DownloadFileDetail fileDetail = new DownloadFileDetail()
{
DocId = dmsFileId
};
string apiUrl = $"files/download/latest/{dmsFileId}";
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(BaseApiUrl);
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {SessionStore.Instance.AuthToken}");
var response = await httpClient.GetByteArrayAsync(apiUrl); --> gone deadlock
fileDetail.Content = response;
return fileDetail;
}

Web Application download from Azure Storage Blobs to Computer

I am trying to download files from azure to computer via an web app. It works when I run locally the project, but when uploaded to ftp server it does not download.
I have tried Environment.SpecialFolder.Peronal, Desktop, etc.
public async Task<bool> DownloadBlobAsync(string file, string fileExtension, string directory)
{
string downlaodPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
_container = _client.GetContainerReference(containerName);
_directoy = _container.GetDirectoryReference(directory);
CloudBlockBlob blockBlob = _directoy.GetBlockBlobReference(file + "." + fileExtension);
using (var fileStream = File.OpenWrite(downlaodPath + "/"+ file + "." + fileExtension))
{
await blockBlob.DownloadToStreamAsync(fileStream);
return true;
}
}
The expected output should be on the documents or desktop.
The issue that you are seeing is due to the fact that your code is executing on the webserver, not on the clients (users) machine.
In other words, when you try to save to Environment.SpecialFolder.Personal, you're trying to save it to that folder on the web server, not the users desktop computer.
What you need to do is return the content of the blob in the request, and let the browser save the file - the user is likely to be prompted (depending on their browser settings) where exactly to save it. You should not be specifying this.
Here is an example of how to do this:
public async Task<HttpResponseMessage> DownloadBlobAsync(string file, string fileExtension, string directory)
{
_container = _client.GetContainerReference(containerName);
_directoy = _container.GetDirectoryReference(directory);
CloudBlockBlob blockBlob = _directoy.GetBlockBlobReference(file + "." + fileExtension);
using (var ms = new MemoryStream())
{
await blockBlob.DownloadToStreamAsync(ms);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.ToArray())
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "somefilename.ext"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue(blockBlob.Properties.ContentType);
return result;
}
}
Note that this is inefficient, as it will download the blob first to the webserver, and then return that to the client. It should be enough to get started.
When this endpoint is hit by the browser, the user will be prompted to save the file somewhere on their PC.

Download a file from a specific library in Sharepoint

I have a simple console application that upload a file from a local folder to a library in sharepoint, and also have a method that download that folder, but using the url that is activated manually in the web site. But I need to download the same file that I upload a second later, it is for a test, AND WHAT I NEED IS TO ACTIVATE THE "VIEW LINK" FOR DOWNLOAD LATER. Here is my upload method:
static void o365SaveBinaryDirect(ClientContext o365Context, string o365LibraryName, string o365FilePath, string o365FileName) {
Web o365Web = o365Context.Web;
if (!LibraryExist(o365Context, o365Web, o365LibraryName)) {
CreateLibrary(o365Context, o365Web, o365LibraryName);
}
using (FileStream o365FileStream = new FileStream(o365FilePath, FileMode.Open)) {
Microsoft.SharePoint.Client.File.SaveBinaryDirect(o365Context, string.Format("/{0}/{1}", o365LibraryName, o365FileName), o365FileStream, true);
}
}
Now I have this method that download:
private static void DownloadFile(string webUrl, ICredentials credentials, string fileRelativeUrl) {
using (var client = new WebClient()) {
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
client.Headers.Add("User-Agent: Other");
client.Credentials = credentials;
client.DownloadFile(webUrl, fileRelativeUrl);
}
}
I need to generate the url for download the file later.
Some suggestions:
avoid generating file url based on library title since library url is
not the same as library title
since Microsoft.SharePoint.Client.File.SaveBinaryDirect method from CSOM
API is used for uploafding a file, consider to utilize Microsoft.SharePoint.Client.File.OpenBinaryDirect method for downloading a file
The following example demonstrates how to upload a file into a library and then downloading it:
var sourceFilePath = #"c:\in\UserGuide.pdf"; //local file path;
var listTitle = "Documents"; //target library;
var list = ctx.Web.Lists.GetByTitle(listTitle);
ctx.Load(list.RootFolder);
ctx.ExecuteQuery();
var targetFileUrl = string.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, Path.GetFileName(sourceFilePath));
//upload a file
using (var fs = new FileStream(sourceFilePath, FileMode.Open))
{
Microsoft.SharePoint.Client.File.SaveBinaryDirect(ctx, targetFileUrl, fs, true);
}
//download a file
var downloadPath = #"c:\out\";
var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctx, targetFileUrl);
var fileName = Path.Combine(downloadPath, Path.GetFileName(targetFileUrl));
using (var fileStream = System.IO.File.Create(fileName))
{
fileInfo.Stream.CopyTo(fileStream);
}

Categories

Resources