Xamarin C# android and PARSE - Downloading a parseFile inside a parseObject - c#

I previously made a post asking how to send a .3gpp audio file up to the parse cloud here:
Xamarin C# Android - converting .3gpp audio to bytes & sending to parseObject
I have managed to do this successfully, on parse's data manager, I can click the file's link and play the sound sent from my android device successfully.
Here's the code for uploading the data to the cloud:
async Task sendToCloud(string filename)
{
ParseClient.Initialize ("Censored Key", "Censored Key");
string LoadPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
string savetheFile = sName + ".3gpp";
string tempUserName;
LoadPath += savetheFile;
Console.WriteLine ("loadPath: " + LoadPath);
try
{
byte[] data = File.ReadAllBytes(LoadPath);
ParseFile file = new ParseFile(savetheFile, data);
await file.SaveAsync();
var auidoParseObject = new ParseObject("AudioWithData");
//Console.WriteLine(ParseUser.getUserName());
if (ParseUser.CurrentUser != null)
{
tempUserName = ParseUser.CurrentUser.Username.ToString();
}
else{
tempUserName = "Anonymous";
}
//tempUserName = ParseUser.CurrentUser.Username.ToString();
Console.WriteLine("PARSE USERNAME: " + tempUserName);
auidoParseObject["userName"] = tempUserName;
auidoParseObject["userName"] = tempUserName;
auidoParseObject["file"] = file;
await auidoParseObject.SaveAsync();
}
catch (Exception e)
{
Console.WriteLine("Failed to await audio object! {0}" + e);
}
}
So as you can see, I'm sending a ParseObject called "AudioWithData".
This object contains two children:
-The username of the user who uploaded the file (string)
-The parseFile called "file" (which has the following two children)
---SaveTheFile (A string containing the name of the audio file, input by the user, with the .3gpp extension added on the end, for example "myAudioFile.3gpp"
---data (this contains the bytes of the audio file)
I need to be able to download the file onto my android device, and play it through a mediaplayer object.
I've checked over the documentation on the parse website, but I haven't managed to do this:
(excuse my pseudo querying syntax here)
SELECT (audio files) FROM (the parseObject) WHERE (the username = current user)
I then, eventually, want to place all of these files into a listview, and when the user clicks the file, it plays the audio.
I've tried the following but I don't really know what I'm doing with it...
async Task RetrieveSound(string filename)
{
ParseClient.Initialize ("Censored key", "Censored key");
Console.WriteLine ("Hit RetrieveSound, filename = " + filename);
string username;
var auidoParseObject = new ParseObject("AudioWithData");
if (ParseUser.CurrentUser != null) {
username = ParseUser.CurrentUser.Username.ToString ();
} else {
username = "Anonymous";
}
string cloudFileName;
Console.WriteLine ("username set to: " + username);
var HoldThefile = auidoParseObject.Get<ParseFile>("audio");
//fgher
var query = from audioParseObject in ParseObject.GetQuery("userName")
where audioParseObject.Get<String>("userName") == username
select file;
IEnumerable<ParseFile> results = await query.FindAsync();
Console.WriteLine ("passed the query");
//wfojh
byte[] data = await new HttpClient().GetByteArrayAsync(results.Url);
Console.WriteLine ("putting in player...");
_player.SetDataSourceAsync (data);
_player.Prepare;
_player.Start ();
}
Any help would be GREATLY APPRECIATED! Even a point in the right direction would be great!
Thanks!
EDIT--
I'm actually getting a query error on the following lines
(I can't post images because of my reputation - I lost access to my main stackOverflow account :/ )
Links to images here:
first error: http://i.stack.imgur.com/PZBJr.png
second error: http://i.stack.imgur.com/UkHvX.png
Any ideas? The parse documentation is vague about this.

this line will return a collection of results
IEnumerable<ParseFile> results = await query.FindAsync();
you either need to iterate through them with foreach, or just pick the first one
// for testing, just pick the first one
if (results.Count > 0) {
var result = results[0];
byte[] data = await new HttpClient().GetByteArrayAsync(result.Url);
File.WriteAllBytes(some_path_to_a_temp_file, data);
// at this point, you can just initialize your player with the audio file path and play as normal
}

Related

Attempting to upload .png file to web directory

I'm trying to use Unity to upload a .png file to a specific web folder. The issue could be 1 of 3 things.
1) I couldn't locate a php.ini file on godaddy. So could it be that the folder isn't configured for upload?
2) I've never done this in Unity before so it could be an issue with my code there. (it does return the message that my file couldn't be uploaded, so it is pinging the .php file)
3) It could also be the php code I have, not sure if I adapted the example properly.
I've read other examples but I'm just not sure where my issue even lies for what I should be fixing. Any advice would be awesome.
Unity:
public IEnumerator UploadTicketImage()
{
byte[] FileUpload = File.ReadAllBytes(Path.Combine(Application.persistentDataPath
+ "/TicketInformation/", "Pic001.png"));
WWWForm TextureForm = new WWWForm();
//TextureForm.AddBinaryData(PlayerPrefs.GetString("ImageName"), FileUpload);
TextureForm.AddBinaryData("TotalTest", FileUpload, Path.Combine(Application.persistentDataPath
+ "/TicketInformation/", "Pic001.png"),"image/png");
UnityWebRequest wwwimageupload = UnityWebRequest.Post(StaticVars.IPAddress+ "/UploadTicketsImage.php?", TextureForm);
wwwimageupload.uploadHandler = (UploadHandler)new UploadHandlerRaw(FileUpload);
//wwwimageupload.chunkedTransfer = false;
yield return wwwimageupload.SendWebRequest();
Debug.Log(wwwimageupload.downloadHandler.text);
}
PHP
<?php
$target_dir = "PhotoDeposit/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
} else {
echo "File is not an image.";
$uploadOk = 0;
}
}
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The file ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
} else {
echo "Sorry, there was an error uploading your file.";
}
?>
I would already expect it to not find the image file you want to upload correctly.
Path.Combine(Application.persistentDataPath + "/TicketInformation/", "Pic001.png")
results in a path like
| |
v v
C:\path\to\project\/TicketInformation/\Pic001.png
on windows or
| |
v v
/path/to/project//TicketInformation//Pic001.png
on linux or android.
It should rather be used like
Path.Combine(Application.persistentDataPath, "TicketInformation", "Pic001.png")
and
Path.Combine(Application.persistentDataPath, "TicketInformation")
The next point is: Why are you even using an UploadHandlerRaw when you already added the data to the WWWForm so this seems to make not much sense to me.
You should remove the line
wwwimageupload.uploadHandler = (UploadHandler)new UploadHandlerRaw(FileUpload);
Also note that File.ReadAllBytes only works on Windows platforms so depending on your build target you might have to change the way you read the file. You could use a UnityWebRequestTexture.GetTexture also to read local files. Something like
public IEnumerator UploadTicketImage()
{
var folderPath = Path.Combine(Application.persistentDataPath, "TicketInformation");
var filepath = Path.Combine(folderPath, "Pic001.png");
byte[] FileUpload = null;
using (var www = UnityWebRequestTexture.GetTexture(filepath))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.LogErrorFormat(this, "File loading failed with {0} - {1}", www.responseCode, www.error);
Debug.LogErrorFormat(this, "Couldn't read file at {0}", filepath);
yield break;
}
else
{
// file data successfully loaded
FileUpload = www.downloadHandler.data;
}
}
var form = new WWWForm();
// here you want the name the file should have on the server
// not a complete system path to file on your local device
// |
// |
// |
// And this is the field you will have |
// to access in php |
// | |
// v v
form.AddBinaryData("TotalTest", FileUpload, "Pic001.png", "image/png");
using (var www = UnityWebRequest.Post("https://111.111.111.1" + "/UploadTicketsImage.php?", form))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.LogError(www.error);
}
else
{
Debug.Log(www.downloadHandler.text);
}
}
}
Finally I am no PHP expert by afaik you will then have to access the submitted field like
$_FILES['TotalTest']
sicne you call the field TotalTest in form.AddBinaryData.
Also the
if(isset($_POST["submit"]))
is probably never true since you didn't set it in the WWWForm (I might be wrong and it is set automatically whenever a request is sent).
Note: Typed on smartphone so no warranty but I hope the idea gets clear.

Android: How do I construct *full* path to pass to Intent.CreateChooser

Trying to make Android chooser to display available actions for user to launch a PDF file which is stored in my local folder.
When I pass the file name like /data/user/0/myappappname/files/output.pdf , (which exsists, of course), I get a nice chooser with all the apps that can accept a pdf file. But when I pick any of them, I get an error (from external app) The document path is not valid. No exception is thrown.
Then I tried (for testing purposes) to set fname to something like /storage/emulated/0/Download/TLCL.pdf (file also exists), and everything works fine.
At first, I thought that this has something to do with file permissions (since first path is private to my app), but then I found flag ActivityFlags.GrantReadUriPermission built exactly for purpose of temporarily granting file access to other apps. Still same results.
Since this is a Xamarin.forms project, I am limited in choice of file creation locations (I use PCLStorage, which always writes to app-private, local folder), so I don't have an option of generating files in /Documents, /Downloads etc.
I am obviously doing something wrong. Any ideas appreciated.
Is there an option to get full path from system, including the /storage/emulated/0 part (or whatever that would be on other devices)? Maybe that would help?
Piece of code:
(mimeType is defined as "application/pdf" earlier)
public async Task<bool> LaunchFile(string fname, string mimeType)
{
var uri = Android.Net.Uri.Parse("file://" + fname );
var intent = new Intent(Intent.ActionView);
intent.SetDataAndType(uri, mimeType);
intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask | ActivityFlags.GrantReadUriPermission );
try
{
Forms.Context.StartActivity(Intent.CreateChooser(intent, "ChooseApp"));
return true;
}
catch (Exception ex)
{
Debug.WriteLine("LaunchFile: " + ex.Message);
return false;
}
My solution to this, which may not be exactly what you want, is to generate a file (in my case a zip file), export it to a public folder, and use that file for the chooser.
Using these:
private readonly string PublicDocsPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/AppName";
private readonly string PrivateDocsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
and some basic functions:
public Stream GetOutputStream(string destFilePath)
{
string destFolderPath = Path.GetDirectoryName(destFilePath);
if (!Directory.Exists(destFolderPath))
Directory.CreateDirectory(destFolderPath);
return new FileStream(destFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
}
public Stream GetInputStream(string sourceFilePath)
{
if (!File.Exists(sourceFilePath)) throw new FileNotFoundException();
string sourceFolderPath = Path.GetDirectoryName(sourceFilePath);
return new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
You can copy your file to your public folder (or subfolders, you just have to assemble the path) and use that file for your chooser:
public void SendEmail(string subject, string body, string recipient, string mimeType, string attachmentFilePath, string activityTitle)
{
var emailIntent = new Intent(Intent.ActionSendMultiple);
if (string.IsNullOrEmpty(subject)) throw new ArgumentException();
emailIntent.PutExtra(Intent.ExtraSubject, subject);
if (!string.IsNullOrEmpty(recipient))
emailIntent.PutExtra(Intent.ExtraEmail, new[] { recipient });
if (!string.IsNullOrEmpty(body))
emailIntent.PutExtra(Intent.ExtraText, body);
if (!string.IsNullOrEmpty(attachmentFilePath))
{
var file = new Java.IO.File(attachmentFilePath);
file.SetReadable(true, true);
var uri = Android.Net.Uri.FromFile(file);
emailIntent.PutParcelableArrayListExtra(Intent.ExtraStream, new List<IParcelable>(){uri});
}
emailIntent.SetType(mimeType);
_activity.StartActivity(Intent.CreateChooser(emailIntent, activityTitle));
}
This chooser specifically lets the user send their file via email or google drive , but you can assemble it however you want. The attachmentFilePath of this function is the same as the string passed into the GetOutputStream function above.
we're using Acr.IO rather than PCLStorage and I recall that has a property that'll return the fullpath for you.
The code we're using is below, but I wonder if you're simply missing "file://" off the start of your path, as I noticed thats in our code, as well as this previous stackoverflow answer to a similar question as this one, open a PDF in Xamarin.Forms (Android)
We're using a dependency FileService on Android and using this code to open PDFs:
public void OpenNatively(string filePath) {
Android.Net.Uri uri;
if (filePath.StartsWithHTTP()) {
uri = Android.Net.Uri.Parse(filePath);
}
else {
uri = Android.Net.Uri.Parse("file:///" + filePath);
}
Intent intent = new Intent(Intent.ActionView);
var extension = filePath.Substring(filePath.LastIndexOf(".")+1);
if (extension == "ppt" || extension == "pptx") {
extension = "vnd.ms-powerpoint";
}
var docType = "application/" + extension;
intent.SetDataAndType(uri, docType);
intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);
try {
Xamarin.Forms.Forms.Context.StartActivity(intent);
}
catch (Exception e) {
Toast.MakeText(Xamarin.Forms.Forms.Context, "No Application found to view " + extension.ToUpperInvariant() + " files.", ToastLength.Short).Show();
}
}

Deep zoom image creation throws exception in c#

Iam building a website which uses a pivot viewer that uses a deep zoom collection to show images and the metadata from a database. Iam using the deezoomtools.dll from microsoft to create the deep zoom collection.
From this website i can start a thread that starts the creation of several collection. For small collections this process goes just fine, however when doing a large collection it fails regularly.
I added some debug lines to see what happens and the exception i get is:
Thread was aborted as the message and as stack trace where it came from.
I found that when the images are processed the exception occurs. I guess that the error occurs in this code sample in the function GetImageForDeepZoomComposer
private bool processImage(Item collectionItem, string backupImage,StreamWriter _logFile)
{
try
{
_logFile.WriteLine("start process");
if (string.IsNullOrEmpty(collectionItem.Image))
return false;
if (!File.Exists(collectionItem.ImagePath))
return false;
_logFile.WriteLine("File is there");
if (!AllImagesUnique) // check to see if we have processed this exact file before.
if (cacheOfImageIds.ContainsKey(collectionItem.ImagePath)) // we already have have processed this image so don't do it again
{
collectionItem.ImageId = cacheOfImageIds[collectionItem.ImagePath];
return true;
}
_logFile.WriteLine("Get Image: " + collectionItem.ImagePath);
string workingImage = GetImageForDeepZoomComposer(collectionItem.ImagePath, collectionItem.Id);
_logFile.WriteLine("Image aquired");
_logFile.WriteLine("collection name and id");
string deepZoomImage = DeepZoomImageDir + collectionItem.Id;
sendAction(string.Format("\tConverting image {0} to Deep Zoom Output file {1} ", workingImage,
deepZoomImage));
//imageCreator.Create(workingImage, deepZoomImage);
_logFile.WriteLine("set image surrogate");
SurrogateImageInfo sii = new SurrogateImageInfo(workingImage, "DeepZoomImages/" + Path.GetFileName(deepZoomImage) + ".xml");
_logFile.WriteLine("image surrogate set");
images.Add(sii);
if (!AllImagesUnique) // if we want to make sure we don't use the Deep Zoom Composer on the same file twice then add this image to the cache
cacheOfImageIds.Add(collectionItem.ImagePath, collectionItem.Id);
return true;
}
catch (Exception ex)
{
_logFile.WriteLine(ex.Message);
sendAction(
string.Format(
"\tSkipping current item cause of execption encountered while processing the image\r\n\t\t{0}",
ex.Message));
return false; // this item will not be added to the collection
}
}
internal string GetImageForDeepZoomComposer(string imageFile,int id)
{
if (imageFile.StartsWith("http://") || imageFile.StartsWith("https://"))
{
if (_client == null) // if we havent used the WebClient for this collection yet create one
_client = new WebClient();
string tempFile = ImageDownloadDir + Guid.NewGuid();
sendAction(string.Format("\tDownloading image '{0} to {1}", imageFile, tempFile));
_client.DownloadFile(imageFile, tempFile);
return tempFile;
}
else if (imageFile.StartsWith("\\") || imageFile.StartsWith("E:\\") || imageFile.StartsWith("e:\\"))
{
var dir = Path.Combine(DeepZoomImageDir, id.ToString() + "_files");
//if (!Directory.Exists(dir))
// Directory.CreateDirectory(dir);
var fixedSizeFileName = imageFile.Replace("_thumb", "_fs");
//create a fixed size image if it's not already created
if (!File.Exists(fixedSizeFileName))
FixedSize(new System.Drawing.Bitmap(imageFile), 512, 512).Save(fixedSizeFileName,codecInfo,parameters);
//copy the fixed size image to the DeepZoomImages folder so it can be used when zooming in to the deepest level
var localFixedSizeImage = Path.Combine(DeepZoomImageDir, id + ".jpg");
File.Copy(fixedSizeFileName, localFixedSizeImage);
return localFixedSizeImage;
}
return imageFile;
}
I think it has something to do with images that are locked when it tries to get the image and tries to save it with a different name such that it could be used. Iam truly at a loss here.
Thank you in advance for the information

How to dowload/upload file onto a users onedrive

I am in a Highschool club where we create windows store apps. I am in charge of the code that allows the user to either download files from their online onedrive storage, or upload files. So far I have successfully logged the user in and gained access to onedrive and display the users name with the following code:
private async void LoadProfile()
{
bool connected = false;
string text = "No Error:";
try
{
var authClient = new LiveAuthClient();
LiveLoginResult result = await authClient.LoginAsync(new List<string>() {"wl.signin", "wl.skydrive"});
if (result.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
var connectClient = new LiveConnectClient(result.Session);
var meResult = await connectClient.GetAsync("me");
dynamic meData = meResult.Result;
Textblock_profilename.Text = meData.name;
}
}
catch (LiveAuthException ex)
{
//Set text to corresponding error
text = ex.ToString();
}
catch (LiveConnectException ex)
{
//Set text to corresponding error
text = ex.ToString();
}
if (text[0].ToString() != "N")
{
var dialog = new Windows.UI.Popups.MessageDialog(text);
await dialog.ShowAsync();
}
}
I gained the code from the following MSDN tutorial: http://msdn.microsoft.com/en-us/library/dn631823.aspx
However when I try to follow the next step, downloading and uploading files, I cannot get it to work. Right now I am just trying to press a button, and have the code download a test file:
private async void Button_downloadFile_Click(object sender, RoutedEventArgs e)
{
try
{
LiveDownloadOperation operation = await connectClient.CreateBackgroundDownloadAsync("skydrive/documents/enter_path");
var result = await operation.StartAsync();
//DO SOMETHING WITH RESULT HERE
}
catch
{
// Handle any errors.
}
}
However this code throws the following errors:
This is straight from the MSDN tutorial, and can't figure out how to fix the error. My best guess is I'm missing a "using" statement, but can't figure out what I am missing. Thanks for any and all help!
Make sure you're updated to use the Live SDK 5.6 binary. Be sure to let us know if you have any other problems with OneDrive integration!

Methods to process asynchronously received form data

I am having trouble wrapping my head around the async/await functionality in .NET 4.5. I am using the code below in a Web API controller to catch multiple files from a form along with some other form data. I have no control over the form or how it sends the data.
What I want to do is receive the files, get data from the form, read a database based on that form data, move the file(s), and update another database table. With the below code I have no trouble getting the files or form data. I get data from the database based on the formID passed in the form data.
It is when I uncomment the code near the bottom for writing back to the database that I run into issues. If I had three files, only one of them gets moved before the catch block catches an exception. I am assuming that my problem is related to the fact that the PostFile method is async.
What is the proper way of writing this code so that it works?
public async Task<HttpResponseMessage> PostFile()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = GetRootPath();
var provider = new MyMultipartFormDataStreamProvider(root);
string logfile = root + "/form_data_output.txt";
try
{
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
string form_id = provider.FormData.Get("FormId");
string driver_id = GetDriverID(form_id); // returns an int as a string
string location = ConfigurationManager.AppSettings["storagePath"];
location += form_id + "\\";
//// make sure the new directory exists
if (!Directory.Exists(location))
{
Directory.CreateDirectory(location);
}
var keys = provider.FormData.Keys.Cast<string>();
foreach (var k in keys.Where(k => k.StartsWith("FormViewer") == true))
{
string filename = provider.FormData.Get(k) + ".pdf";
string type_key = "FormType_" + k.Substring(k.IndexOf('_') + 1);
string type_value = provider.FormData.Get(type_key);
// setup the full path including filename
string path = root + "\\" + filename;
string newFullPath = location + filename;
// move the file
File.Move(path, newFullPath);
if (File.Exists(newFullPath))
{
if (File.Exists(newFullPath))
{
try
{
string conn_str = ConfigurationManager.ConnectionStrings["eMaintenanceConnection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(conn_str))
{
SqlCommand cmd = new SqlCommand("INSERT INTO eSubmittal_Document VALUES (null,#driver_id,#location,#doctype)");
cmd.Parameters.AddWithValue("#driver_id", driver_id);
cmd.Parameters.AddWithValue("#location", location);
cmd.Parameters.AddWithValue("#doc_type", type_value);
conn.Open();
int c = await cmd.ExecuteNonQueryAsync();
conn.Close();
}
}
catch (Exception e)
{
LogEntry(logfile, e.Message);
}
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
async and await provide natural program flow for asynchronous code. So for the most part, you can just think about code the way you normally think about it:
It is when I uncomment the code near the bottom for writing back to the database that I run into issues. If I had three files, only one of them gets moved before the catch block catches an exception.
Here's what I get from this:
Your database code is throwing an exception.
When the exception is thrown, it leaves the foreach loop to go to the catch handler.
Nothing unexpected there...
It's hard to help very much without knowing the exception, but performing a synchronous database operation inside an async method seems like a bad idea to me. Try changing your code to use:
int c = await cmd.ExecuteNonQueryAsync();

Categories