I'm opening a file via:
await Windows.System.Launcher.LaunchFileAsync(storageFile, options);
The documentation for LaunchFileAsync says:
When the launch fails for any of the above reasons, the API succeeds
and returns FALSE from its asynchronous operation. Since it has no
ability to query whether the above restrictions apply to the current
launch, the calling app should not assume that the launch succeeded,
and should provide fallback mechanism in case it failed. A possible
solution would be to ask the user to save the file and direct the user
to open it in the desktop.
What's the most straightforward way to do that?
I tried:
var picker = new FolderPicker();
var pfolder = await picker.PickSingleFolderAsync();
StorageApplicationPermissions.FutureAccessList.Add(pfolder);
var folder = await StorageFolder.GetFolderFromPathAsync(pfolder.Path);
var file = await folder.CreateFileAsync(storageFile.Name);
using (var writer = await file.OpenStreamForWriteAsync())
{
await writer.WriteAsync(storageFile,0,0);
}
But unfortuantely writer.WriteAsync only takes Bytes[] and not the StorageFile. How do I get my StorageFile saved?
I am busy writing an application where the user needs to capture a lot of images and then they get packaged together with some text data and then they get uploaded to a local server. I want to implement the uploading on the Android platform through an Intent Service but I cannot find a good Xamarin Forms PCL example to show me how.
This is the method where I initialize the Intent to pass to the IntentService:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
Intent uploadIntent = new Intent();
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
And this is the IntentService itself.
[Service]
public class ServiceIntent : IntentService
{
public ServiceIntent() : base("ServiceIntent")
{
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
return base.OnStartCommand(intent, flags, startId);
}
public override void OnCreate()
{
base.OnCreate();
}
protected override void OnHandleIntent(Intent intent)
{
Uri serviceAddress = new Uri(intent.GetStringExtra("serviceAddress"));
Guid captureId = Guid.Parse(intent.GetStringExtra("captureId"));
CaptureEntity capture = new DatabaseConnection_Android().CreateConnection().Query<CaptureEntity>("SELECT * FROM [CaptureEntity]").Single(c => c.WorkflowId == captureId);
var images = new DatabaseConnection_Android().CreateConnection().Query<ImageEntity>("SELECT * FROM [ImageEntity]").Where(i => i.CaptureEntityId == capture.Id);
try
{
MultipartFormDataContent content = new MultipartFormDataContent();
StringContent strContent = new StringContent(
capture.XmlData,
Encoding.UTF8,
"text/xml");
IImageHandler handler = new ImageHandler_Droid();
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("workflow", capture.WorkflowId.ToString());
request.Method = HttpMethod.Post;
request.RequestUri = serviceAddress;
foreach (var image in images)
{
byte[] imageByte = handler.ReadAllBytes(image.ImagePath);
ByteArrayContent byteContent = new ByteArrayContent(imageByte);
byteContent.Headers.Add("Content-Type", "image/jpeg");
content.Add(byteContent, "file", image.ImageName);
}
content.Add(strContent, "text/xml");
request.Content = content;
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(180);
var response = client.SendAsync(
request,
HttpCompletionOption.ResponseContentRead).Result;
var readResponse = response.Content.ReadAsStringAsync().Result;
if (readResponse == "File uploaded.")
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Completed",
"Success");
else if (readResponse.Contains("An error has occurred."))
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Uploader",
String.Format(CultureInfo.InvariantCulture,
"Failed: {0}",
readResponse));
else
MessagingCenter.Send<CaptureEntity, string>(
capture,
"Uploader",
String.Format(CultureInfo.InvariantCulture,
"Failed: {0}",
readResponse));
}
}
catch (WebException webExc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
webExc.Message));
}
catch (TimeoutException timeExc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
timeExc.Message));
}
catch (Exception exc)
{
MessagingCenter.Send<string, string>("Uploader", "Failed",
String.Format(CultureInfo.InvariantCulture,
"{0} upload failed.\n{1}",
capture.DisplayName,
exc.Message));
}
}
}
Can anyone tell me what I am doing wrong as I am getting the following error when I want to start the service:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
In your Intent declaration you need to tell the service you want to call
Something like this:
var uploadIntent = new Intent(this, typeof(ServiceIntent));
Note: this represents the Context.
Update:
As mentioned in the comments your interface implementation cannot derive from Activity class. In order to have access to the Context to be able to call the StartService method and also create your Intent you can make it in two ways:
Using the Xamarin.Forms.Forms.Context:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
var context = Xamarin.Forms.Forms.Context;
var uploadIntent = new Intent(context, typeof(ServiceIntent));
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
context.StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
If you are using latest versions of Xamarin.Forms this global context was deprecated and they suggest to you local context instead. You can still use it though but in future updates of XF your app might break.
using CurrentActivity plugin:
public async Task<bool> UploadAsync(Uri serviceAddress,
CaptureEntity capture,
List<ImageEntity> images)
{
try
{
var context = CrossCurrentActivity.Current.Activity;
var uploadIntent = new Intent(context, typeof(ServiceIntent));
uploadIntent.PutExtra("serviceAddress", serviceAddress.ToString());
uploadIntent.PutExtra("captureId", capture.WorkflowId.ToString());
context.StartService(uploadIntent);
return true;
}
catch (Exception exc)
{
App.logger.LogError(DateTime.Now, "Uploader", exc.ToString());
throw exc;
}
}
This plugin can be installed from nugget and the setup is very straight forward. Basically it gives you access to the current activity and you can use it as your context to call the IntentService
Hope this helps.-
Here is the IntentService.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
In Android, we usually use IntentService to do asynchronous operator. As we all know, thread is also used to do asynchronous operator. The difference between IntentService and Thread is IntentService is Service which belongs to Android Component. So, the priority of IntentService is higher than Thread.
For example, there is a ActivityA which has a IntentService, and there is a ActivityB which has a Thread, both IntentService and Thread are working, and both ActivityA and ActivityB are al background Activity. Now, if your phone's system doesn't have extra resources, your ActivityB will be killed firstly.
About the Exception:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName android.content.Context.startService(android.content.Intent)' on a null object reference
That means you should use android.content.Context to call the StartService method. In Android, there are three kinds of Context. Application, Activity and Service. So you can call the StartService method in these three classes directly. If you are not in these three classes, you need pass the Context to your class, and then use the Context to call StartService.
I added Activity for this class' inheritance.
If you do this, your class will be a Activity, and you need to register it in your manifiest, add layout for your class, and it should have the lifecycle, and etc. It will not be what you want to get class. In Android, Activity is a Component, not normal class, so you can't inherit it unless you want your class to be a Activity.
Demo:
I have made a demo for you,
The goal:
What I want to do is drag a GridViewItem from a GridView in a UWP app outside of the app to the desktop explorer. During the drop event, I want to download a file from the internet and create a StorageFile that will be used to populate the DataPackage. I want this StorageFile to be copied to the desktop. Unfortunately, when a deferral is used for UWP Drag&Drop (using SetDataProvider), as soon as you leave the app window, the request is activated and you have to populate the DataPackage with the object to be transferred. So, it seemed to me that I would need to use a deferred type of StorageFile created with CreateStreamedFileFromUriAsyc.
I do not want to pre-download the data every time I start to do a drag and drop operation. I only want to download the data when I'm actually dropping it somewhere legitimate to copy.
I know how to drag and drop a pre-existing StorageFile from UWP to Explorer (desktop) using a deferred request.
I also know how to create a StorageFile using CreateStreamedFileFromUriAsyc that will gives you a StorageFile that downloads data only when the data is requested.
When I try to combine these two ideas, windows Explorer gives me the error 'interface is not supported.'.
If I use the exact same code, but just get the file contents by calling something like GetBasicPropertiesAsync during the deferred drag handler, it works only if I hold the drag over the desktop until the file is downloaded. I can see it finishing when the drag icon changes from the 'prohibited' icon to 'copy'. If I let go of the mouse button before it is done, no copy will occur and no errors are raised.
Obviously, I would like the drag and drop to download without having to manually start it on the deferred handler. Any ideas? Is this possible?
(Yes, I realize the code to create the correct file extension is wrong/incomplete, but that's irrelevant here...)
//DragStarted Handler in constructor
DragItemsStartedCommand = ReactiveCommand.Create<DragItemsStartingEventArgs>((e) =>
{
_dragItems = e.Items.Cast<ItemViewModel>();
e.Data.Properties.Title = "Transfer file";
e.Data.Properties.Description = "desscription of transfering a file";
e.Data.Properties.FileTypes.Add(StandardDataFormats.StorageItems);
e.Data.SetDataProvider(Windows.ApplicationModel.DataTransfer.StandardDataFormats.StorageItems, OnDeferredStorageFileRequestedHandler);
e.Data.RequestedOperation = DataPackageOperation.Copy;
});
//Deferred request handler
async void OnDeferredStorageFileRequestedHandler(DataProviderRequest request)
{
DataProviderDeferral deferral = request.GetDeferral();
try
{
Task<IEnumerable<StorageFile>> task = null;
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
task = DownloadStorageFiles();
});
var result = await task;
request.SetData(result);
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
deferral.Complete();
Debug.WriteLine("deferral complete!!!");
}
}
//Create StorageFile with deferred loading Task
async Task<IEnumerable<StorageFile>> DownloadStorageFiles()
{
List<StorageFile> storageItems = new List<StorageFile>();
foreach (var item in _dragItems)
{
var request = new RestSharp.RestRequest();
var defaultItemType = ItemType.MSWord;
switch (item.MimeTypeTranslated)
{
case ItemType.GoogleDocument:
case ItemType.GoogleSpreadsheet:
case ItemType.GooglePresentation:
case ItemType.GoogleDrawing:
case ItemType.GoogleScript:
request.Resource = $"files/{item.File.id}/export";
request.AddParameter("mimeType", Statics.ItemTypeDictionary.First(x => x.Value == defaultItemType).Key);
break;
default:
request.Resource = $"files/{item.File.id}";
request.AddParameter("alt", "media");
break;
}
string fileName = "";
if (item.File.name.EndsWith($".{Statics.ItemExtensionDictionary[defaultItemType]}"))
fileName = $"{item.File.name}";
else
fileName = $"{item.File.name}.{Statics.ItemExtensionDictionary[defaultItemType]}";
var uri = account.Client.GetAuthorizedUriForDownload(request);
var thumbnail = RandomAccessStreamReference.CreateFromFile(await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///StoreLogo.png")));
var storageFileDeferred = await StorageFile.CreateStreamedFileFromUriAsync(fileName, uri , thumbnail);
//var props = await storageFileDeferred.GetBasicPropertiesAsync();
storageItems.Add(file);
}
return storageItems;
}
GitHub repro of this problem:
https://github.com/limefrogyank/DragDeferredFileToDesktop
First page is a regular drag to desktop that works because StorageFile (+ underlying data) is already in assets folder
Second page shows the error generated when using a StorageFile that is created with CreateStreamedFileFromUriAsync.
Third page uses the same type of StorageFile, but with a hack to force the retrieval of data synchronously. Desktop freezes for a second until data is ready.
As of the recent Windows updates the drag n' drop of storage files on UWP apps is automatically handled by the system.
example gif.
I am using loopback Storage component REST API in Xamarin to finish a file uploading job. However, it does not work and does not return any exceptions to me.
Here is my code:
library using: RestSharp.portable
public async Task addFiles(string name, byte[] file)
{
try
{
var client = new RestClient(App.StrongLoopAPI);
var request = new RestRequest("containers/container1/upload", HttpMethod.Post);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "multipart/form-data");
request.AddFile("file", file, name + ".jpg", System.Net.Http.Headers.MediaTypeHeaderValue.Parse("multipart/form-data"));
var res = await client.Execute(request);
}
catch (Exception ex)
{
//return null;
}
}
Does my function have any problems?
You're setting the Content Type (Mime Type) wrong.
AddFile accepts as the last parameter the Content Type (for example image/jpeg for a JPG image), where you're using multipart/form-data.
There are different ways to figure out the Content Type of a file, see here:
Get MIME type from filename extension
This should fix your issue.
I am writing a program that will be executing on the cloud. The program will generate an output that should be written on to a file and the file should be saved on the blob container.
I don't have a idea of how to do that
Will this code
FileStream fs = new FileStream(file, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
generate a file named "file" on the cloud...
Oh.. then how to store the content to the blob..
Are you attempting to upload a Page blob or a Block blob? Usually block blobs are what are required, unless you are going to create a VM from the blob image, then a page blob is needed.
Something like this works however. This snippet taken from the most excellent Blob Transfer Utility Check it out for all your upload and download blob needs. (Just change the type from Block To Page if you need a VHD)
public void UploadBlobAsync(ICloudBlob blob, string LocalFile)
{
// The class currently stores state in class level variables so calling UploadBlobAsync or DownloadBlobAsync a second time will cause problems.
// A better long term solution would be to better encapsulate the state, but the current solution works for the needs of my primary client.
// Throw an exception if UploadBlobAsync or DownloadBlobAsync has already been called.
lock (WorkingLock)
{
if (!Working)
Working = true;
else
throw new Exception("BlobTransfer already initiated. Create new BlobTransfer object to initiate a new file transfer.");
}
// Attempt to open the file first so that we throw an exception before getting into the async work
using (FileStream fstemp = new FileStream(LocalFile, FileMode.Open, FileAccess.Read)) { }
// Create an async op in order to raise the events back to the client on the correct thread.
asyncOp = AsyncOperationManager.CreateOperation(blob);
TransferType = TransferTypeEnum.Upload;
m_Blob = blob;
m_FileName = LocalFile;
var file = new FileInfo(m_FileName);
long fileSize = file.Length;
FileStream fs = new FileStream(m_FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
ProgressStream pstream = new ProgressStream(fs);
pstream.ProgressChanged += pstream_ProgressChanged;
pstream.SetLength(fileSize);
m_Blob.ServiceClient.ParallelOperationThreadCount = 10;
asyncresult = m_Blob.BeginUploadFromStream(pstream, BlobTransferCompletedCallback, new BlobTransferAsyncState(m_Blob, pstream));
}