c# UWP Save StorageFile Without Dialog - c#

I am in trouble with some issue about FileSavePicker. Is there any solution about saving a StorageFile without showing any popup or dialog to ask user. I want to give the current path of the storage file from code behind.
var byteArray = Convert.FromBase64String(Base64);
StorageFile file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("file.jpg", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteBytesAsync(file, byteArray);
var savePicker = new FileSavePicker();
savePicker.FileTypeChoices.Add("JPEG-Image", new List<string>() { ".jpg" });
savePicker.SuggestedSaveFile = file;
savePicker.PickSaveFileAsync();

...We have to use a few paths which are defined under
'Windows.Storage.KnownFolders'...
it is not so. In fact your app can access any folder on device but it will need additional permissions. The most straightforward way to obtain permission you should do next:
1) ask user to pick folder from FolderPicker
2) store selected folder to StorageApplicationPermissions.FutureAccessList
After this your app can do anything with this folder.
Code that demonstrate how to obtain permissions:
var picker = new FolderPicker();
var pfolder = await picker.PickSingleFolderAsync();
StorageApplicationPermissions.FutureAccessList.Add(pfolder);
Code that demonstrate how to create file in desired folder:
var folder = await StorageFolder.GetFolderFromPathAsync("your path");
var file = await folder.CreateFileAsync("text.txt");
using (var writer = await file.OpenStreamForWriteAsync())
{
await writer.WriteAsync(new byte[100], 0, 0);
}
But keep in mind that "your path" is folder or any of subfolder that was stored to StorageApplicationPermissions.FutureAccessList. More details here FutureAccessList

StorageFile.GetFileFromPathAsync() is probably what you are looking for. I think the file must be created beforehand for this method to work. Be aware that creating the file in some locations might need additional permissions or is even impossible (I don't know how UWP and UAC interact).
Another possibility is to use Win32 APIs inside UWP apps. Maybe this can solve your problem if the desired API is available.

I want to thank you all for your interest. I figured out the solution. If you do not want to use any dialog or prompting you can not use FileSavePicker. Here are the simple codes you need to converting Base64 string to image and save.
var byteArray = Convert.FromBase64String(Base64);
StorageFile file = await Windows.Storage.KnownFolders.SavedPictures.CreateFileAsync(
"file.jpg", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteBytesAsync(file, byteArray);
I guess there is no way to give specific path. We have to use a few paths which are defined under Windows.Storage.KnownFolders. If it is not, please give some information about it.

Related

Accessing and creating/deleting files in Pictures Library?

I am attempting to create a file in the Pictures Library as documented in Microsofts UWP api.
The Pictures Library typically has the following path.
%USERPROFILE%\Pictures
I have enabled the Pictures library capability in the app manifest.
Like so:
File.WriteAllBytes("%USERPROFILE%/Pictures", fileData);
It returns:
DirectoryNotFoundException: Could not find a part of the path 'C:\Data\Users\DefaultAccount\AppData\Local\DevelopmentFiles\HoloViewerVS.Debug_x86.jtth.jh\%USERPROFILE%\Pictures'
`
When I try using my username hard-coded, like so:
File.WriteAllBytes("jtth.jh/Pictures", fileData);
it returns the same DirectoryNotFound exception:
DirectoryNotFoundException: Could not find a part of the path 'C:\Data\Users\DefaultAccount\AppData\Local\DevelopmentFiles\HoloViewerVS.Debug_x86.jtth.jh\jtth.jh\Pictures'`
So, how do you access the Pictures Library and write files to it? Clearly I must be missing something small here as the path its trying to access the pictures library at seems quite odd here.
I see it says how to 'Get the Pictures library.' using a StorageFolder:
public static StorageFolder PicturesLibrary { get; }
But I'm not sure how to add files to this folder variable like the way I'm doing it using the file write.
Can I do it the way I am trying to do it, or do I need to jump through StorageFolder and/or async hoops?
Example for clarification:
string imgName = currentFolderLoaded + "/" + temp.GetComponentInChildren<Text>().text;
//example imgName to enhance clairty
imgName = C:\dev\Users\jtth\Hololens\ProjectFolder\App\HoloApp\Data\myFiles\pics\example.png
//load image
byte[] fileData;
Texture2D tex = null;
fileData = File.ReadAllBytes(imgName);
tex = new Texture2D(2, 2);
tex.LoadImage(fileData);
//test hololens access
//previous attempt -> File.WriteAllBytes("%USERPROFILE%/Pictures", fileData);
//New attempt
AsStorageFile(fileData, imgName);
//Read Texture into RawImage component
ImgObject.GetComponent<RawImage>().material.mainTexture = tex;
//Enable component to render image in game
ImgObject.GetComponent<RawImage>().enabled = true;
Function:
private static async Task<StorageFile> AsStorageFile(byte[] byteArray, string fileName)
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.KnownFolders.PicturesLibrary;
Windows.Storage.StorageFile sampleFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteBytesAsync(sampleFile, byteArray);
return sampleFile;
}
I'm checking the Photos app on the Hololens after running through this code and it doesn't seem to be creating the picture?
If you want your pictures showed in the Photos app on Hololens, actually you may need to save the pictures into CameraRoll folder.
But it seems like it cannot be directly saved, you may need to move the picture file as a workaround.
var cameraRollFolder = Windows.Storage.KnownFolders.CameraRoll.Path;
File.Move(_filePath, Path.Combine(cameraRollFolder, _filename));
Details please reference this similar thread.
It is because UWP applications work under isolated storage. Like the first example in your linked MSDN article says, do something like this:
StorageFolder storageFolder = KnownFolders.PicturesLibrary;
StorageFile file = await storageFolder.CreateFileAsync("sample.png", CreationCollisionOption.ReplaceExisting);
// Do something with the new file.
Plain File.Xxx() calls will be isolated to your application's own little world.

copying and replacing files in uwp

I am creating an app and one of the requirements for the app was to be able to allow the users to change the data that the app has access to by means of changing a folder in the documents folder and pressing an update button. 1. This app will not have access to the internet. 2. Accessing the Documents folder is a specific request(i know uwp is sand-boxed) 3. Essentially I'm trying to moves files from a folder in the documents storage to local storage without the path(since I dont have access to it). This is my attempt at it.
class Copy
{
public async void update()
{
FolderPicker openPicker = new FolderPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".csv");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".txt");
StorageFolder updatefolder = await openPicker.PickSingleFolderAsync();
//CreateFileAsync("sample.dat", CreationCollisionOption.ReplaceExisting);
// Do something with the new file.
// Get the app's local folder to use as the destination folder.
StorageFolder mainFolder = ApplicationData.Current.LocalFolder;
// Set options for file type and sort order.
List<string> fileTypeFilter = new List<string>();
fileTypeFilter.Add(".jpg");
fileTypeFilter.Add(".csv");
fileTypeFilter.Add(".png");
fileTypeFilter.Add(".txt");
QueryOptions queryOptions = new QueryOptions(CommonFileQuery.OrderByName, fileTypeFilter);
queryOptions.IndexerOption = IndexerOption.OnlyUseIndexer;
// Get the files in the user's document folder
// and its subfolders and sort them by date.
StorageFileQueryResult results = updatefolder.CreateFileQueryWithOptions(queryOptions);
// Iterate over the results and print the list of files
// to the Visual Studio Output window.
IReadOnlyList<StorageFile> sortedFiles = await results.GetFilesAsync();
sortedFiles.ToList<StorageFile>();
foreach (StorageFile item in sortedFiles)
{
StorageFile copiedFile = await item.CopyAsync(mainFolder, item.DisplayName, NameCollisionOption.ReplaceExisting);
}
}
}
}
This kind of works. When I call the update method, the files copy but don't replace even though I have the CreationCollisionOption.ReplaceExisting option for CreateFileAsync. Also the thumbnails don't transfer over and even though it has the same memory the computer can't recognize what type of file it is. Here is a screenshot.Destination Folder I thought it was due to the list being readonly so I tried explicitly converting it into a list but I received the same result. I'm so close and I just need a little assistance. Any and all help will be greatly appreciated.

OpenStreamForReadAsync fails with "The Parameter is Incorrect" from root

I'm trying to figure out the proper way to load resources that I have included in my package. There are a few other questions similar to this (e.g. Unable to access Asset files in Metro app), but I'd like to avoid having to manually construct arbitrary ms-appx:\\\ paths if possible.
// The location of everything in my package.
StorageFolder packageLocation = Package.Current.InstalledLocation;
// The folder I want to load a file from
StorageFolder resources = await packageLocation.GetFolderAsync("Resources");
// I can successfully find the file, and then open a stream.
StorageFile file = await resources.GetFileAsync("Default.xml");
Stream streamFromFile = await file.OpenStreamForReadAsync();
// Also, I can just directly open a stream for the file from the folder.
Stream streamFromFolder = await resources.OpenStreamForReadAsync("Default.xml");
// Error: The parameter is incorrect
Stream streamFromRoot = await packageLocation.OpenStreamForReadAsync("Resources/Default.xml")
I've tried many combinations including ./Resources/..., /Resources/..., ms-appx:///Resources/.... Why doesn't it work from the root folder?
Note: I haven't verified it yet, but I feel like I have the same issue with other 'root' folders such as ApplicationData.Current.LocalFolder.
Please test with the following syntax.
var file = await packageLocation.GetFileAsync(#"Resources\Default.xml");
or using stream
var stream = await packageLocation.OpenStreamForReadAsync(#"Resources\Default.xml");
As a follow up to the answer provided by Jean-Sebastien I wanted to point out a few other scenarios where slashes matter.
If you use the StorageFile.GetFileFromApplicationUriAsync API, this accepts a URI parameter which can be prefixed with ms-appx:///.
var uri = new Uri(#"ms-appx:///Resources\DefaultPickerView.xml");
var file1 = await StorageFile.GetFileFromApplicationUriAsync(uri);
Note that when you create the URI the back slashes are all converted into forward slashes.
On the other hand, I was unable to get StorageFile.GetFileFromPathAsync to work at all, regardless of what combination of prefixes and slashes I used. The GetFileFromPathAsync MSDN documentation however, does indicate that you can't use forward slashes in your path.

DownloadsFolder crashes if using CreationCollisionOption.ReplaceExisting

I create a file that I want to be automatically saved to the Downloads folder of a user. So I used:
StorageFile file = await DownloadsFolder.CreateFileAsync("filename.txt");
But if I create that file again, I get an error stating that file already exists. So I used:
StorageFile file = await DownloadsFolder.CreateFileAsync("filename.txt", CreationCollisionOption.ReplaceExisting);
However that also crashes with the error saying that the parameter does not fall within the expected range. But if I use:
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("filename.txt", CreationCollisionOption.ReplaceExisting);
Then it does create the file even if it exists, but I need to create/save the file in the Downloads folder for easier access for the user. I thought of this (attempted) solution because of the issue described here prevented me from adding an attachment to certain e-mail clients.
The method CreateFileAsync (String) uses the FailIfExists option as the default. So when you create that file again, you get an error stating that file already exists.
For more info, see CreationCollisionOption.
The DownloadsFolder.CreateFileAsync("filename.txt", CreationCollisionOption.ReplaceExisting) method crashes with the error saying that the parameter does not fall within the expected range.
Because the app can only access files in the Downloads folder that it created, you can't specify OpenIfExists or ReplaceExisting for this parameter.
Please see DownloadsFolder.CreateFileAsync(String, CreationCollisionOption) document’s Parameters section(the last paragraph).
You can use DownloadsFolder.CreateFileAsync("filename.txt", CreationCollisionOption.GenerateUniqueName) to create the file.
It can automatically append a number to the base of the specified name if the file or folder already exists.
If you want to overwrite the exists file in DownloadsFolder. You can use FileSavePicker.PickSaveFileAsync to returns a storageFile object that was created to represent the saved file, and use FileIO.WriteTextAsync(IStorageFile, String) method to write text to the specified file. It can overwrite the old file.
For example:
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.Downloads;
savePicker.FileTypeChoices.Add("Simple Line Files", new List<string>() { ".txt" });
savePicker.SuggestedFileName = "filename";
StorageFile file = await savePicker.PickSaveFileAsync();
if (file != null)
{
await FileIO.WriteTextAsync(file, "hello");
}

Can I reference local files in a webview when I use navigatetostring?

I'm using a webview to display certain data in my windows 8 app. I would like to user an include to a local js file as well as use locally stored images.
Is this possible?
I haven't had any luck by putting the local path where the files are located.
According to WebView documentation you can only reference other files using the ms-appx-web protocol, i.e. to load the files stored in Windows.ApplicationModel.Package.Current.InstalledLocation, meaning that they need to be distributed as content along with your application. The control doesn't support ms-appdata protocol for security reasons, i.e. you can't open files stored Windows.Storage.ApplicationData.Current.LocalFolder Windows.Storage.ApplicationData.Current.RemoteFolder or Windows.Storage.ApplicationData.Current.TempFolder where you'd need to put them if you were generating or downloading them at runtime.
In JavaScript apps WebView is a bit more flexible: it does support ms-appdata protocol as well, but only for media files such as images. It cannot open any potentially executable code, such as script or CSS.
If you want to open some local .html file or atc. you should download it in InstalledLocation folder. If you haven't option to create a new file you can just use file.CopyAsync(htmlFolder, fname + ".html");
For example I create some .html file:
StorageFolder htmlFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.CreateFolderAsync(#"HtmlFiles", CreationCollisionOption.GenerateUniqueName);
IStorageFile file = await htmlFolder .CreateFileAsync(fname + ".html", CreationCollisionOption.GenerateUniqueName);
and than I can easily open this .html file with or without FileOpenPicker:
var fop = new FileOpenPicker();
fop.FileTypeFilter.Add(".html");
var file = await fop.PickSingleFileAsync();
if (file != null)
{
string myPath = file.Path.Substring(file.Path.IndexOf("HtmlFiles"));
myWebview.Navigate(new Uri("ms-appx-web:///" + myPath));
}
And don't forget - just only from InstalledLocation you can open it with ms-appx-web:///
If the WebView is IE10 based, FIleReader may be what you are looking for. Here is a snippet of code that I use on an image ipload page to show images in a page when they are selected via a File Open dialog:
$('input:file').each(function(index, evt){
if(index===0)
{
var files = evt.files;
for(var i=0;i<files.length;i++)
{
if(files[i].name===filename)
{
var reader = new FileReader();
reader.onload=(function(theFile){
return function(e){
var line= uploader.fineUploader('getItemByFileId',id);
if(line!=undefined)
$(line).append('<img class="fileimage" id="fileImage-' + id + '" src="'+e.target.result+'" />');
};
})(files[i]);
reader.readAsDataURL(files[i]);
break;
}
}
}
I hope this points you in the right direction!

Categories