I am developing an Windows Runtime Universal Application.
I need to upload the database I'm using in my application to the user's OneDrive?
But FileNotFound exception occurs. But I know the path is correct.
Since I refer the sqlite file, it shows the exception. If I refer txt file, Uploading process goes smoothly.
var authClient = new LiveAuthClient();
var authResult = await authClient.LoginAsync(new string[] { "wl.skydrive", "wl.skydrive_update" });
if (authResult.Session != null)
{
var liveConnectClient = new LiveConnectClient(authResult.Session);
var FileToUpload = await ApplicationData.Current.LocalFolder.GetFileAsync("text.sqlite");//exception occurs here
var FileToUpload = await ApplicationData.Current.LocalFolder.GetFileAsync("text.txt");//no exception for txt files
var folderData = new Dictionary<string, object>();
folderData.Add("name", "Folder")
LiveOperationResult operationResult = await liveConnectClient.PostAsync("me/skydrive", folderData);
LiveUploadOperation uploadOperation = await liveConnectClient.CreateBackgroundUploadAsync(folderId, "filename", FileToUpload, OverwriteOption.Overwrite);
LiveOperationResult uploadResult = await uploadOperation.StartAsync();
HandleUploadResult(uploadResult);
}
First, you should use a background transfer task to move a file that large and to handle the occasion when the user gets a call during transfer. Background tasks are the key.
http://code.msdn.microsoft.com/windowsapps/Background-Transfer-Sample-d7833f61/sourcecode?fileId=52027&pathId=1495533284
Sort of like this.
BackgroundUploader uploader = new BackgroundUploader();
UploadOperation upload = uploader.CreateUpload(uri, file);
await HandleUploadAsync(upload, true);
But before you even do that, you need to assume that the user will manipulate the database. As a result, you should copy the database file before you start the upload. Again, code:
var sourceFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
var sourceFile = await sourceFolder.CreateFileAsync("database",
Windows.Storage.CreationCollisionOption.OpenIfExists);
var targetFolder = await sourceFolder.CreateFolderAsync("~",
Windows.Storage.CreationCollisionOption.OpenIfExists);
var targetFile = await targetFolder.CreateFileAsync(sourceFile.Name,
Windows.Storage.CreationCollisionOption.ReplaceExisting);
await sourceFile.MoveAndReplaceAsync(targetFile);
This is not only a good idea, I have a feeling it will correct the problems you are having. Remember you can always query and ask if a transfer is done (do you aren't doing two).
Best of luck!
Related
I'm currently trying to implement several tasks that involve listing, uploading and downloading files from a shared OneDrive folder. This folder is accesible via the logged in users OneDrive (visible in his root folder). The listing part works pretty well so far, using this code:
string remoteDriveId = string.Empty;
private GraphServiceClient graphClient { get; set; }
// Get the root of the owners OneDrive
DriveItem ownerRoot = await this.graphClient.Drive.Root.Request().Expand("thumbnails,children($expand=thumbnails)").GetAsync();
// Select the shared folders general information
DriveItem sharedFolder = ownerRoot.Children.Where(c => c.Name == "sharedFolder").FirstOrDefault();
// Check if it is a remote folder
if(sharedFolder.Remote != null)
{
remoteDriveId = item.RemoteItem.ParentReference.DriveId;
// Get complete Information of the shared folder
sharedFolder = await graphClient.Drives[remoteDriveId].Items[sharedFolder.RemoteItem.Id].Request().Expand("thumbnails,children").GetAsync();
}
So obviously I need to retrieve the shared folders information from the OneDrive that shared it with the other OneDrive.
Next part for me is to list the contents of this shared folder, which also works pretty well like this:
foreach (DriveItem child in sharedFolder.Children)
{
DriveItem childItem = await graphClient.Drives[remoteDriveId].Items[child.Id].Request().Expand("thumbnails,children").GetAsync();
if(childItem.Folder == null)
{
string path = Path.GetTempPath() + Guid.NewGuid();
// Download child item to path
}
}
My problem starts with the "Download child item to path" part. There I want to download everything, that is not a folder to a temporary file. The problem is that OneDrive always answers my request with an error message, that the file was not found. What I tried so far is:
using (var stream = await graphClient.Drives[remoteDriveId].Items[childItem.Id].Content.Request().GetAsync())
using (var outputStream = new System.IO.FileStream(path, System.IO.FileMode.Create))
{
await stream.CopyToAsync(outputStream);
}
In another variant I tried to use the ID of the childItem ParentReference (but I think this will only lead me to the remote OneDrives ID of sharedFolder):
using (var stream = await graphClient.Drives[remoteDriveId].Items[childItem.ParentReference.Id].Content.Request().GetAsync())
using (var outputStream = new System.IO.FileStream(path, System.IO.FileMode.Create))
{
await stream.CopyToAsync(outputStream);
}
After Downloading the files I want to edit them and reupload them to a different path in the shared folder. That path is created by me (which allready works) like this:
DriveItem folderToCreate = new DriveItem { Name = "folderName", Folder = new Folder() };
await graphClient.Drives[remoteDriveId].Items[sharedFolder.Id].Children.Request().AddAsync(folderToCreate);
The upload then fails. I've tried it like this:
using (var stream = new System.IO.FileStream(#"C:\temp\testfile.txt", System.IO.FileMode.Open))
{
await graphClient.Drives[remoteDriveId].Items[sharedFolder.Id].Content.Request().PutAsync<DriveItem>(stream);
}
And also like this (which works if it is not a shared folder and I therefore use Drive instead of Drives):
using (var stream = new System.IO.FileStream(#"C:\temp\testfile.txt", System.IO.FileMode.Open))
{
string folderPath = sharedFolder.ParentReference == null ? "" : sharedFolder.ParentReference.Path.Remove(0, 12) + "/" + Uri.EscapeUriString(sharedFolder.Name);
var uploadPath = folderPath + "/" + uploadFileName;
await graphClient.Drives[remoteDriveId].Root.ItemWithPath(uploadPath).Content.Request().PutAsync<DriveItem>(stream);
}
I couldn't get the AddAsync method (like in the folder creation) to work because I don't know how to create a DriveItem from a Stream.
If somebody could point me in the right direction I would highly appreciate that! Thank you!
The request:
graphClient.Drives[remoteDriveId].Items[childItem.ParentReference.Id].Content.Request().GetAsync()
corresponds to Download the contents of a DriveItem endpoint and is only valid if childItem.ParentReference.Id refers to a File resource, in another cases it fails with expected exception:
Microsoft.Graph.ServiceException: Code: itemNotFound Message: You
cannot get content for a folder
So, to download content from a folder the solution would be to:
enumerate items under folder: GET /drives/{drive-id}/items/{folderItem-id}/children
per every item explicitly download its content if driveItem corresponds to a File facet: GET /drives/{drive-id}/items/{fileItem-id}/content
Example
var sharedItem = await graphClient.Drives[driveId].Items[folderItemId].Request().Expand(i => i.Children).GetAsync();
foreach (var item in sharedItem.Children)
{
if (item.File != null)
{
var fileContent = await graphClient.Drives[item.ParentReference.DriveId].Items[item.Id].Content.Request()
.GetAsync();
using (var fileStream = new FileStream(item.Name, FileMode.Create, System.IO.FileAccess.Write))
fileContent.CopyTo(fileStream);
}
}
Example 2
The example demonstrates how to download file from a source folder and upload it into a target folder:
var sourceDriveId = "--source drive id goes here--";
var sourceItemFolderId = "--source folder id goes here--";
var targetDriveId = "--target drive id goes here--";
var targetItemFolderId = "--target folder id goes here--";
var sourceFolder = await graphClient.Drives[sourceDriveId].Items[sourceItemFolderId].Request().Expand(i => i.Children).GetAsync();
foreach (var item in sourceFolder.Children)
{
if (item.File != null)
{
//1. download a file as a stream
var fileContent = await graphClient.Drives[item.ParentReference.DriveId].Items[item.Id].Content.Request()
.GetAsync();
//save it into file
//using (var fileStream = new FileStream(item.Name, FileMode.Create, System.IO.FileAccess.Write))
// fileContent.CopyTo(fileStream);
//2.Upload file into target folder
await graphClient.Drives[targetDriveId]
.Items[targetItemFolderId]
.ItemWithPath(item.Name)
.Content
.Request()
.PutAsync<DriveItem>(fileContent);
}
}
Instead of downloading/uploading file content, i think what you are actually after is DriveItem copy or move operations. Lets say there are files that needs to be copied from one (source) folder into another (target), then the following example demonstrates how to accomplish it:
var sourceDriveId = "--source drive id goes here--";
var sourceItemFolderId = "--source folder id goes here--";
var targetDriveId = "--target drive id goes here--";
var targetItemFolderId = "--target folder id goes here--";
var sourceFolder = await graphClient.Drives[sourceDriveId].Items[sourceItemFolderId].Request().Expand(i => i.Children).GetAsync();
foreach (var item in sourceFolder.Children)
{
if (item.File != null)
{
var parentReference = new ItemReference
{
DriveId = targetDriveId,
Id = targetItemFolderId
};
await graphClient.Drives[sourceDriveId].Items[item.Id]
.Copy(item.Name, parentReference)
.Request()
.PostAsync();
}
}
}
I'm a newbie in UWP and i want to open a file of any type and transmit the bytes of it to the reciever. forexample for a jpg file i wrote this code:
// Create FileOpenPicker instance
FileOpenPicker fileOpenPicker = new FileOpenPicker();
// Set SuggestedStartLocation
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
// Set ViewMode
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.FileTypeFilter.Clear();
fileOpenPicker.FileTypeFilter.Add(".jpg");
// Open FileOpenPicker
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
byte[] bytesRead = File.ReadAllBytes(file.Path);
string Paths =
#"C:\\Users\zahraesm\Pictures\sample_reconstructed.jpg";
File.WriteAllBytes(Paths, bytesRead);
the two last lines are for writing the bytes into a file supposing in the receiver. However i keep getting the following exception:
System.InvalidOperationException: 'Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run.'
Try this Code.
try {
FileOpenPicker openPicker = new FileOpenPicker {
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
FileTypeFilter = { ".jpg", ".jpeg", ".png" }
};
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null) {
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read)) {
var reader = new Windows.Storage.Streams.DataReader(fileStream.GetInputStreamAt(0));
var LoadReader = await reader.LoadAsync((uint)fileStream.Size);
byte[] pixels = new byte[fileStream.Size];
reader.ReadBytes(pixels);
}
}
} catch (Exception ex) {
}
consider wrapping last operation in Task.Run()
await Task.Run(()=>{
byte[] bytesRead = File.ReadAllBytes(file.Path);
string Paths =
#"C:\\Users\zahraesm\Pictures\sample_reconstructed.jpg";
File.WriteAllBytes(Paths, bytesRead);
});
You should directly read the bytes from the StorageFile returned from your FilePicker, lest you end up with File permission errors in the future.
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
var buffer = await FileIO.ReadBufferAsync(file);
byte[] bytes = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer);
You should also use await FileIO.WriteBytesAsync(targetFile, myBytes) to write.
Unless you have broadFileSystemAccess in your package Manifest, you should generally avoid using the System.IO API unless you know your application explicitly has permission to access files in that area (i.e., your application's local storage), and instead use Windows.Storage API's
Check MSDN for File Access Permissions for UWP apps for more information on file permissions.
And if you do use System.IO, always perform the work on the background thread via await Task.Run(() => { ... }
I'm working on a windows client for uploading a lot of small files over an http post request.
I’m using .NET 4.5.2
public async void Upload3(HttpClient client, string url, string[] files)
{
foreach (var file in files)
{
using (var stream = new FileStream(file, FileMode.Open))
{
FileInfo info = new FileInfo(file);
HttpContent fileStreamContent = new StreamContent(stream);
using (var content = new MultipartFormDataContent())
{
content.Add(fileStreamContent);
var response = await client.PostAsync(url, content);
response.EnsureSuccessStatusCode();
//code is stopping at the following line:
string finalresults = await response.Content.ReadAsStringAsync();
Console.WriteLine(finalresults);
Console.WriteLine(" > Uploaded file " + info.Name);
}
stream.Close();
}
}
Console.WriteLine("> Uploaded all files");
}
The Code is working fine for the very first file. But every other file is not uploaded. When I try to debug the code step by step, the code execution stops (in the second iteration of the loop) on this line:
string finalresults = await response.Content.ReadAsStringAsync();
Since the server log only shows on single request, I think that the error already occurs in this line:
var response = await client.PostAsync(url, content);
Even if I use different HttpClient objects and different FileStream objects, the upload is only working for the first file.
What is wrong with this code?
For your requiment, you can user third party libraries like RESTSharp. There are lots of examples and good documentation. Also it is easy to use.
In my windows phone application i'm trying to create a new contact and add an image that taken from the camera, but it didn't seems to work.
No matter what i do the photo is blank.
The only way it works for me is by using an image that i added in the assets folder (and not from camera), even trying to add the image to the local assets folder and then to upload it - don't work...
(there is no error, but the contact that was added don't have a photo).
await contact.SetDisplayPictureAsync(stream.AsStream().AsInputStream());
Here is my code:
when i get the selected image from store i save it to a bitmapImage and use its pixel buffer.
public async void AddContact(string displayName, string mobile, string email, byte[] data)
{
var store = await ContactStore.CreateOrOpenAsync();
var contact = new StoredContact(store)
{
DisplayName = displayName
};
var props = await contact.GetPropertiesAsync();
props.Add(KnownContactProperties.Email, email);
props.Add(KnownContactProperties.MobileTelephone, mobile);
using (var stream = bitmap.PixelBuffer.AsStream())
{
await contact.SetDisplayPictureAsync(stream.AsInputStream());
}
await contact.SaveAsync();
}
Please help!
I was doing the same to set profile picture in for contact and it was not working. But when I get IRandomAccessStream from storage file it worked here is what I am doing
var file = await folder.GetFileAsync("filename.jpeg"));
//Or you can get file direclty from localfolder
// var file = await ApplicationData.Current.LocalFolder.GetFileAsync("filename.jpeg");
using (Windows.Storage.Streams.IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
await contact.SetDisplayPictureAsync(fileStream);
}
await contact.SaveAsync();
Edit
How to Save picture to local storage using media capture.
ImageEncodingProperties imgFormat = ImageEncodingProperties.CreateJpeg();
//Save file to local storage
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
"MyPhoto.jpg", CreationCollisionOption.ReplaceExisting);
await mediaCapture.CapturePhotoToStorageFileAsync(imgFormat, file);
Once the image is saved in local storage you can get that image from me first example
Edit 2
If you are using File open picker you can try this.
public async void ContinueFileOpenPicker(Windows.ApplicationModel.Activation.FileOpenPickerContinuationEventArgs args)
{
if (args.Files.Count > 0)
{
var filePath = args.Files[0].Path;
StorageFile file = await Windows.Storage.StorageFile.GetFileFromPathAsync(filePath);
using (Windows.Storage.Streams.IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
await contact.SetDisplayPictureAsync(fileStream);
}
}
I'm having a problem with inclomplete blobs being downloaded from Azure storage. The files that are stored are an images. Almost every file that's downloaded ends up missing several lines on the bottom. I've checked the blobs and they were uploaded correctly.
I'm using the following code for downloading a blob from the Azure service:
private async Task Download(CloudBlobClient client)
{
try
{
_media = await _directory.CreateFileAsync(ResourceName, CreationCollisionOption.FailIfExists);
}
catch (Exception)
{
return;
}
using (var stream = await _media.OpenAsync(FileAccessMode.ReadWrite))
{
var blob = await GetBlob(client);
await blob.DownloadToStreamAsync(stream);
_category.NotifyAzureProgress();
await stream.FlushAsync();
}
}
The method GetBlob() looks like this:
private async Task<CloudBlockBlob> GetBlob(CloudBlobClient client)
{
CloudBlobContainer container = client.GetContainerReference(ContainerName);
await container.CreateIfNotExistsAsync();
var blob = container.GetBlockBlobReference(ResourceName);
return blob;
}
Upload code:
private async Task UploadAsync(CloudBlobClient client)
{
_media = await _directory.GetFileAsync(ResourceName);
using (var stream = await _media.OpenAsync(FileAccessMode.Read))
{
var blob = await GetBlob(client);
await blob.UploadFromStreamAsync(stream);
_category.NotifyAzureProgress();
}
}
Thanks for any help!
Edit: I've realized I've missed out one detail - the downloaded image has correct dimensions, but several lines from the bottom are black - it doesn't has the same pixels as the source image. I've checked the MD5 hashes and while they match, when I download the image through an external app, they don't match when I download them with the code above.
Edit2: after inspecting the properties of CloudBlob and the output stream, I've noticed, that even though the blob gives correct length after download, the stream usually says something a little lower. I've tried downloading throught range, but to no avail
Ok, so I've managed to download the images afterall, by partially using the WinRT Azure library combined with a standard .NET HttpClient.
I used the Azure Lib establish the initial connection and then to get only the Blob reference, because the BlockBlobReference has a method to create Shared Access Signature (and I really didn't want to try to construct it myself). Then I created the HttpClient, made a download URL using the SAS and issued a GET request to the URL, which finally worked and downloaded all the images intact.
I think there might be some weird bug in the official library, since using my download method instead of theirs solved everything.
Code sample:
internal async Task Download(CloudBlobClient client)
{
try
{
_media = await _directory.CreateFileAsync(ResourceName, CreationCollisionOption.FailIfExists);
}
catch (Exception)
{
return;
}
try
{
var blob = await GetBlob(client);
HttpClient httpClient = new HttpClient();
var date = DateTime.UtcNow;
var policy = new SharedAccessBlobPolicy();
policy.Permissions = SharedAccessBlobPermissions.Read;
policy.SharedAccessStartTime = new DateTimeOffset(date);
policy.SharedAccessExpiryTime = new DateTimeOffset(date.AddDays(1));
var signature = blob.GetSharedAccessSignature(policy);
var uriString = string.Format("{0}{1}", blob.Uri.ToString(), signature);
var data = await httpClient.GetByteArrayAsync(uriString);
var buf = new Windows.Storage.Streams.Buffer((uint)data.Length);
await FileIO.WriteBytesAsync(_media, data);
_category.NotifyAzureProgress();
}
catch (Exception e)
{
_media.DeleteAsync();
throw e;
}
}