Xamarin Unauthorized Access Permission In downloading a file - c#

I've been trying to create a function where the user will download a file(PDF) when a button is clicked.
I stored the file in firebase storage and can be accessible via url/link. I found this solution How to download files in Xamarin.Forms? that helps you download from a url. However I got an error that say **System.UnauthorizedAccessException:** 'Access to the path '/data/user/0/com.companyname.pawadopt_v5/files' is denied.' I already made sure to check and request permission using Xamarin.Essentials but I keep getting this error even with Permission.Granted for StorageRead and StorageWrite.
Here is my code:
Download Function
public async Task<bool> DownloadFile(string fileURL)
{
var checkPermission = await PermissionServices.PermissionClientInstance.checkStorage();
if(checkPermission == true)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
try
{
var client = new HttpClient();
var downloadStream = await client.GetStreamAsync(fileURL);
var fileStream = File.Create(path);
await downloadStream.CopyToAsync(fileStream);
return true;
}
catch (Exception ex)
{
return false;
}
}
else
{
return false;
}
}
Check and Request Permission
var Readpermission = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
var Writepermission = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (Readpermission != PermissionStatus.Granted || Writepermission != PermissionStatus.Granted)
{
Readpermission = await Permissions.RequestAsync<Permissions.StorageRead>();
Writepermission = await Permissions.RequestAsync<Permissions.StorageWrite>();
}
if (Readpermission != PermissionStatus.Granted && Writepermission != PermissionStatus.Granted)
return false;
else
return true;
What are your thoughts and solutions about this?
Any ideas and solution are greatly appreciated
UPDATE
When I changed the path into string localPath = Path.Combine(FileSystem.AppDataDirectory,"File.pdf");, No error shows and prompt the 'Download Successful'. However I cant find where this local path is.

Related

Unable to open a realm at path xxx in Xamarin forms

I'm reading data from remote MongoDB realm which syncs to my local realm, but it seems I can't read from my local realm after sync.
This is the message I get when I try to read from my local realm:
Unable to open a realm at path '/data/user/0/com.companyname.appname/files/default.realm': Incompatible histories. Expected a Realm with no or in-realm history, but found history type 3 Path:Exception backtrace:\n<backtrace not supported on this platform>.
Here is my code:
private async Task<Realm> OpenRealm()
{
try
{
var user = App.realmApp.CurrentUser;
//if user is not logged on yet log on the user and sync
if (user == null)
{
var CurrentUser = await App.realmApp.LogInAsync(Credentials.Anonymous());
var config = new SyncConfiguration("Hirschs", CurrentUser);
_realm = await Realm.GetInstanceAsync(config);
return _realm;
}
else
{
return _realm = Realm.GetInstance();
}
}
catch (Exception ex)
{
await UserDialogs.Instance.AlertAsync(new AlertConfig
{
Title = "An error has occurred",
Message = $"An error occurred while trying to open the Realm: {ex.Message}"
});
// Try again
return await OpenRealm();
}
}
The problem here is that you are trying to create a new local realm in the same path where the synced realm already is.
I suppose that you would like to open the same realm synchronously (that is necessary if the device is offline). In this case you would just need to use the same configuration for both the sync and async calls, as reported in the documentation here.
You could do something like:
private async Task<Realm> OpenRealm()
{
try
{
var currentUser = App.realmApp.CurrentUser;
if (currentUser == null)
{
var currentUser = await App.realmApp.LogInAsync(Credentials.Anonymous());
var config = new SyncConfiguration("Hirschs", currentUser);
_realm = await Realm.GetInstanceAsync(config);
return _realm;
}
else
{
var config = new SyncConfiguration("Hirschs", currentUser);
_realm = Realm.GetInstance(config);
return _realm;
}
}
catch (Exception ex)
{
await UserDialogs.Instance.AlertAsync(new AlertConfig
{
Title = "An error has occurred",
Message = $"An error occurred while trying to open the Realm: {ex.Message}"
});
}
}

How to resolve image attachment display issue in skype bot?

I am using bot framework (C# SDK) for my bot in skype. It works fine in the emulator but when i run it on the skype, it does not display image attachemnts.
I have checked documentation but i couldn't find any solution. I am not sure if it is the problem on my end or in the skype.
Note: I am using skype for windows 10 and skype for android on my cell phone (does not work in either of these)
Here is http response for attachement in emulator
Here is the image in skype
Here is the method that generates attachement
public Attachment ChartCard(StandardInfoData[] data)
{
if (data != null && data.Length != 0)
{
string chartUrl = data[0]?.report?.url;
Attachment attachment = new Attachment();
attachment.ContentUrl = chartUrl;
attachment.ContentType = "image/png";
attachment.Name = "Chart Report";
return attachment;
}
else
{
Attachment attachment = new Attachment();
attachment.Content = "No Chart Report Found";
return attachment;
}
}
Here is the code that calls this method. I am getting this image from our server "Human Collaborator" and using sockets to communicate with it.
For sockets i am using "websocket-sharp" library
private async Task StandardInfoFormComplete(IDialogContext context, IAwaitable<StandardInfoForm> result)
{
try
{
var form = await result;
eventOccured = false;
SocketStream.ws.OnMessage += (sender, e) =>
{
try
{
var json = e.Data;
StandardInfoJson response = JsonConvert.DeserializeObject<StandardInfoJson>(json);
var data = response.data;
if (data[0].result.ToLower() == "ok")
{
// For chart reports
var chartAttachment = card.ChartCard(data);
replyToConversation.Attachments.Add(chartAttachment);
context.PostAsync(replyToConversation);
}
if (data[0].result.ToLower() == "error")
{
var replyToConversation = context.MakeMessage();
var message = data[0]?.message;
replyToConversation.Text = message;
context.PostAsync(replyToConversation);
}
}
catch (NullReferenceException ex)
{
Debug.WriteLine(ex.StackTrace);
}
eventOccured = true;
};
//builds the botRequest part of json string
Utilities.StringBuilder.JsonStringBuilder(context, result);
// Instantiate JSONDataObjects class
JSONDataObjects jdo = new JSONDataObjects();
//Create JSON string
string output = JsonConvert.SerializeObject(jdo, Formatting.Indented);
Debug.WriteLine("USER: \n" + output);
//Sending the Json object to Human-Collaborator
SocketStream.ws.Send(output);
await context.PostAsync("Generating response.....Please Be Patient.....");
Thread.Sleep(8000);
if (!eventOccured)
await context.PostAsync("Server is busy ... Please try again later");
context.Done(true);
}
catch (Exception e)
{
string reply;
if (e.InnerException == null)
{
reply = $"You quit --maybe you can finish next time!";
}
else
{
reply = "Sorry, I've had a short circuit. Please try again.";
}
await context.PostAsync(reply);
}
}
Please help me what i am doing wrong. Thanks
I don't have all the code to replay your case but I see one important thing:
else
{
Attachment attachment = new Attachment();
attachment.Content = "No Chart Report Found";
return attachment;
}
Here you should not create an attachment as you have nothing to attach. This will throw an error because the ContentType is missing.
Can you modify the code that is calling your ChartCard: it should not call it when its parameter (your StandardInfoData[]) is empty.
I am not able to test more with the code you provided, but you should also check if your data in the Socket call is okay when calling from Skype (like by outputting the value or any other way), it will help in your investigation
EDIT: after a lot of comments on this answer, looks like the problem comes from the fact that the image is not reachable from Skype (located on a server inside user's network)

Web API Upload Files

I have some data to save into a database.
I have created a web api post method to save data. Following is my post method:
[Route("PostRequirementTypeProcessing")]
public IEnumerable<NPAAddRequirementTypeProcessing> PostRequirementTypeProcessing(mdlAddAddRequirementTypeProcessing requTypeProcess)
{
mdlAddAddRequirementTypeProcessing rTyeProcessing = new mdlAddAddRequirementTypeProcessing();
rTyeProcessing.szDescription = requTypeProcess.szDescription;
rTyeProcessing.iRequirementTypeId = requTypeProcess.iRequirementTypeId;
rTyeProcessing.szRequirementNumber = requTypeProcess.szRequirementNumber;
rTyeProcessing.szRequirementIssuer = requTypeProcess.szRequirementIssuer;
rTyeProcessing.szOrganization = requTypeProcess.szOrganization;
rTyeProcessing.dIssuedate = requTypeProcess.dIssuedate;
rTyeProcessing.dExpirydate = requTypeProcess.dExpirydate;
rTyeProcessing.szSignedBy = requTypeProcess.szSignedBy;
rTyeProcessing.szAttachedDocumentNo = requTypeProcess.szAttachedDocumentNo;
if (String.IsNullOrEmpty(rTyeProcessing.szAttachedDocumentNo))
{
}
else
{
UploadFile();
}
rTyeProcessing.szSubject = requTypeProcess.szSubject;
rTyeProcessing.iApplicationDetailsId = requTypeProcess.iApplicationDetailsId;
rTyeProcessing.iEmpId = requTypeProcess.iEmpId;
NPAEntities context = new NPAEntities();
Log.Debug("PostRequirementTypeProcessing Request traced");
var newRTP = context.NPAAddRequirementTypeProcessing(requTypeProcess.szDescription, requTypeProcess.iRequirementTypeId,
requTypeProcess.szRequirementNumber, requTypeProcess.szRequirementIssuer, requTypeProcess.szOrganization,
requTypeProcess.dIssuedate, requTypeProcess.dExpirydate, requTypeProcess.szSignedBy,
requTypeProcess.szAttachedDocumentNo, requTypeProcess.szSubject, requTypeProcess.iApplicationDetailsId,
requTypeProcess.iEmpId);
return newRTP.ToList();
}
There is a field called 'szAttachedDocumentNo' which is a document that's being saved in the database as well.
After saving all data, I want the physical file of the 'szAttachedDocumentNo' to be saved on the server. So i created a method called "UploadFile" as follows:
[HttpPost]
public void UploadFile()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
// Get the uploaded file from the Files collection
var httpPostedFile = HttpContext.Current.Request.Files["UploadedFile"];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
string folderPath = HttpContext.Current.Server.MapPath("~/UploadedFiles");
//string folderPath1 = Convert.ToString(ConfigurationManager.AppSettings["DocPath"]);
//Directory not exists then create new directory
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// Get the complete file path
var fileSavePath = Path.Combine(folderPath, httpPostedFile.FileName);
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
}
Before running the project, i debbugged the post method, so when it comes to "UploadFile" line, it takes me to its method.
From the file line, it skipped the remaining lines and went to the last line; what means it didn't see any file.
I am able to save everything to the database, just that i didn't see the physical file in the specified location.
Any help would be much appreciated.
Regards,
Somad
Makes sure the request "content-type": "multipart/form-data" is set
[HttpPost()]
public async Task<IHttpActionResult> UploadFile()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
MultipartMemoryStreamProvider provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.Contents != null && provider.Contents.Count == 0)
{
return BadRequest("No files provided.");
}
foreach (HttpContent file in provider.Contents)
{
string filename = file.Headers.ContentDisposition.FileName.Trim('\"');
byte[] buffer = await file.ReadAsByteArrayAsync();
using (MemoryStream stream = new MemoryStream(buffer))
{
// save the file whereever you want
}
}
return Ok("files Uploded");
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}

SQLiteDatabase import Windows Universal

I'm trying to import an excisting SQLitedatabase in my Windows Universal project.I followed along this tutorial. Which does just what I want.
However it states:
then copy the database with a .sqlite extension to the root of the shared project in your universal app.
So I added my excisting databse to the root of my Shared Project
However when I try the following code I get an IOException the File could not be found.
private async Task CopyDatabase()
{
bool isDatabaseExisting = false;
try
{
StorageFile storageFile = await ApplicationData.Current.LocalFolder.GetFileAsync("FixedCamerasOK.sqlite");
isDatabaseExisting = true;
}
catch(Exception ex)
{
isDatabaseExisting = false;
}
if (!isDatabaseExisting)
{
StorageFile databaseFile = await Package.Current.InstalledLocation.GetFileAsync("FixedCamerasOK.sqlite");
await databaseFile.CopyAsync(ApplicationData.Current.LocalFolder);
}
}
So where do I place the .sqlite file so it can be found.
Add the File As Content instead as none or empty
Search for the file(FixedCamerasOk.sqlite) in the Installed folder to see if it exists
public async Task<bool> DoesDatabaseExist()
{
bool dbexists = true;
try
{
var files = Package.Current.InstalledLocation.GetFilesAsync();
var retvalues = (from f in await files
where f.Name == "FixedCamerasOk.sqlite"
select f);
int count = retvalues.Count();
if (count > 0)
return dbexists;
else
return false;
}
catch (Exception)
{
dbexists = false;
}
return dbexists;
}
First select your file and in properties select Build Action as Content then open the file like this
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///FixedCamerasOK.sqlite"));

Creating a load tester for an upload API with UploadFileAsync

What I am trying to accomplish here is to see how much this API can handle as far as requests per second. I am trying to consume the API in a console app that will ultimately be throwaway code. My idea was to make a for loop that would try to upload an xml document every 2 seconds. I've never done this sort of thing before so forgive my ignorance. Here's my Main method:
static void Main()
{
RunAsync().Wait();
}
And the RunAsync method:
static async Task RunAsync()
{
Uri apiUrl = new Uri("http://apiurl.com/upload/files/uploadfiles");
const string file = #"C:\simple.xml";
WebClient client = new WebClient();
for (int i = 0; i <= 100; i++)
{
client.UploadFileCompleted += FileUploadSuccess;
client.UploadFileAsync(apiUrl, file);
await Task.Delay(2000);
Console.WriteLine("Upload waiting 2 seconds...");
}
Console.WriteLine("Loop completed.");
}
And the success method:
private static void FileUploadSuccess(object sender, UploadFileCompletedEventArgs e)
{
string reply = System.Text.Encoding.UTF8.GetString(e.Result);
Console.WriteLine("The file result was: {0}", reply);
}
It throws an exception on the first time through on e.Result. Here's the exception:
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in System.dll
After doing some research, apparently I can't call the API method (which returns an async Task) without await'ing it. Unfortunately it seems UploadFileAsync is not "awaitable."
Here's the API method:
public async Task<HttpResponseMessage> UploadFiles()
{
var pilotTokenObject = TokenHelper.CreatePilotTokenObject(Request);
byte[] fileBuffer = null;
HttpResponseMessage retVal = null;
if (pilotTokenObject != null)
{
var content = Request.Content;
if (content == null)
{
throw new PilotApiException("Empty request content", HttpStatusCode.NoContent);
}
if (!content.IsMimeMultipartContent())
{
throw new PilotApiException("Request does not contain not multi-part content");
}
var uploadModelController = new PilotUploadModelController();
//*SAVE STREAMED FILE*
string serverSavePath = ConfigurationManager.AppSettings["PilotUploadApiTempStoragePath"];
if (!Directory.Exists(serverSavePath))
Directory.CreateDirectory(serverSavePath);
var provider = new MultipartFormDataStreamProvider(serverSavePath);
await Request.Content.ReadAsMultipartAsync(provider);
var fileData = provider.FileData;
if (fileData == null || fileData.Count == 0)
{
throw new PilotApiException("No multipart/form file data present.");
}
bool uploaded = false;
//Loop through each file
fileData.ForEach((fileRequest) =>
{
if (RetryUntilFileReadable(Path.Combine(serverSavePath, fileRequest.LocalFileName), 1000, 5))
{
var fileHeader = fileRequest.Headers;
if (fileHeader != null && fileHeader.ContentDisposition != null)
{
var fileName = fileHeader.ContentDisposition.FileName.Replace("\"", "");
var fileBytes = File.ReadAllBytes(Path.Combine(serverSavePath, fileRequest.LocalFileName));
//Save File to DB
var upload = uploadModelController.UploadHelper
.AddUploadFileToDb(pilotTokenObject.CentralUserDbUserId, pilotTokenObject.ClientIp, pilotTokenObject.UserAgentString,
UploadEnums.UploadKind.PilotUploadApi, fileName, fileBytes.Length, fileBytes,
UploadEnums.EncryptionType.None);
if (upload != null)
uploaded = true;
}
}
});
if (uploaded)
{
retVal = Request.CreateResponse(HttpStatusCode.Accepted, new
{
Response = String.Format("file uploaded successfully.")
});
}
}
return retVal;
}
Am I going about this the completely wrong way? Is what I want to do even doable?
It seems to me that the following would work better in your scenario:
byte[] response = await Task.Run(() => client.UploadFile(apiUrl, file));
string reply = System.Text.Encoding.UTF8.GetString(response);
Console.WriteLine("The file result was: {0}", reply);
Console.WriteLine("Upload waiting 2 seconds...");
await Task.Delay(2000);
Trying to mix-and-match the older asynchronous API with the newer async/await doesn't seem fruitful in this case. Better to just wrap the synchronous version of the API with async/await-compatible code.
(Note that it seems to me you could just as well call Thread.Sleep(2000) instead of creating a new delay task to wait on, but the above should work fine too).

Categories