How to copy a folder in a Windows Store App - windows-8

How do you copy a folder and its contents in Windows Store Apps?
I'm writing tests for a Windows Store App. The app does things with files, so a set of known files is needed. Ideally, any developer could run these tests without requiring that they do some manual setup.
I assume that means test files would be checked into source control and then copied to the LocalState folder where tests could consume them (copy during ClassInitialize test phase).
StorageFile has copy functions. It would be possible to to use these to recursively rebuild the folder to copy. However it's hard to believe that this would be the correct approach... surely I'm missing something.

This is rough and not thoroughly tested. It copies folders recursively. For name collisions, it overwrites existing files and folders.
public static async Task CopyAsync(
StorageFolder source,
StorageFolder destination)
{
// If the destination exists, delete it.
var targetFolder = await destination.TryGetItemAsync(source.DisplayName);
if (targetFolder is StorageFolder)
await targetFolder.DeleteAsync();
targetFolder = await destination.CreateFolderAsync(source.DisplayName);
// Get all files (shallow) from source
var queryOptions = new QueryOptions
{
IndexerOption = IndexerOption.DoNotUseIndexer, // Avoid problems cause by out of sync indexer
FolderDepth = FolderDepth.Shallow,
};
var queryFiles = source.CreateFileQueryWithOptions(queryOptions);
var files = await queryFiles.GetFilesAsync();
// Copy files into target folder
foreach (var storageFile in files)
{
await storageFile.CopyAsync((StorageFolder)targetFolder, storageFile.Name, NameCollisionOption.ReplaceExisting);
}
// Get all folders (shallow) from source
var queryFolders = source.CreateFolderQueryWithOptions(queryOptions);
var folders = await queryFolders.GetFoldersAsync();
// For each folder call CopyAsync with new destination as destination
foreach (var storageFolder in folders)
{
await CopyAsync(storageFolder, (StorageFolder)targetFolder);
}
}
Please, someone have a better answer. Copying a folder should be a one line call to a tested .net API. We shouldn't all have to write our own functions or copy-paste untested code from the internet.

Here is my version for copying folders, if has 3 extension methods on IStorageFolder:
shallow copy of all files and folders
recursive copy of all files and folders
copy just files
Code:
public static class StorageFolderExtensions
{
/// <summary>
/// Recursive copy of files and folders from source to destination.
/// </summary>
public static async Task CopyContentsRecursive(this IStorageFolder source, IStorageFolder dest)
{
await CopyContentsShallow(source, dest);
var subfolders = await source.GetFoldersAsync();
foreach (var storageFolder in subfolders)
{
await storageFolder.CopyContentsRecursive(await dest.GetFolderAsync(storageFolder.Name));
}
}
/// <summary>
/// Shallow copy of files and folders from source to destination.
/// </summary>
public static async Task CopyContentsShallow(this IStorageFolder source, IStorageFolder destination)
{
await source.CopyFiles(destination);
var items = await source.GetFoldersAsync();
foreach (var storageFolder in items)
{
await destination.CreateFolderAsync(storageFolder.Name, CreationCollisionOption.ReplaceExisting);
}
}
/// <summary>
/// Copy files from source into destination folder.
/// </summary>
private static async Task CopyFiles(this IStorageFolder source, IStorageFolder destination)
{
var items = await source.GetFilesAsync();
foreach (var storageFile in items)
{
await storageFile.CopyAsync(destination, storageFile.Name, NameCollisionOption.ReplaceExisting);
}
}

Related

How do you upload 260kb PDF file to Azure Blob Storage using ASP.NET and .NET based Controller?

I currently am trying to upload a pdf file of size 260kb with Swagger UI and it doesnt work. If I try to do the same thing with a small 50kb Word file it works.
My controller code is:
[HttpPost()]
public async Task<IActionResult> Upload(IFormFile file)
{
var name = SanitizeFilename(file.FileName);
if (String.IsNullOrWhiteSpace(name))
{
throw new ArgumentException();
}
using (Stream stream = file.OpenReadStream())
{
await storage.Save(stream, name);
}
return Accepted();
}
My AzureBlobStorage class's save method is:
public async Task<Task> Save(Stream fileStream, string name)
{
var blobContainer = await GetBlobContainerAsync();
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(name);
var task = blockBlob.UploadFromStreamAsync(fileStream);
var success = task.IsCompletedSuccessfully;
return task;
//return blockBlob.UploadFromStreamAsync(fileStream);
}
Here is some of the debug windows:
This is from the controller of the word document:
This is from the controller of the PDF document:
Notice the red/pink lettering which is different.
This is from the AzureBlobStorage save method - word document:
This is from the AzureBlobStorage save method - pdf document:
I have read the IFormFile might not do continuous streaming but how do I know if that is the issue? And if it is, what is the preferred approach?
I am not following your logic here:
public async Task<Task> Save(Stream fileStream, string name)
{
var blobContainer = await GetBlobContainerAsync();
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(name);
var task = blockBlob.UploadFromStreamAsync(fileStream);
var success = task.IsCompletedSuccessfully;
return task;
//return blockBlob.UploadFromStreamAsync(fileStream);
}
This is the way it should be written:
public async Task Save(Stream fileStream, string name)
{
var blobContainer = await GetBlobContainerAsync();
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(name);
await blockBlob.UploadFromStreamAsync(fileStream);
}
You want to await for the task to finish here before you return.
Returning Task<Task> is slightly unorthodox and doesn't make sense for what you want to do here.
Also, keep in mind, if your file is really large, Kestrel server could give up on the request. There is a timeout in the range of around 90 seconds to complete the request. So, if uploading the file takes longer than 90 seconds, the caller could receive an error (but the upload will still finish).
Typically you will dump the file to the disk, then return an Accepted to the caller. Then post the file to a background queue to upload the file. More information about that here.

Tewr.Blazor.FileReader - File Name

I am currently using the following to upload the files selected. However, I would like to use the file name of the local file for the server file name. I have not been able to discover how to recover that name.
public async Task ReadFile()
{
foreach (var file in await fileReaderService.CreateReference(inputTypeFileElement).EnumerateFilesAsync())
{
// Read into buffer and act (uses less memory)
await using (Stream stream = await file.OpenReadAsync())
{
buffer = new Byte[stream.Length];
// Do (async) stuff with stream...
await stream.ReadAsync(buffer);
}
// Read file fully into memory and act
using (MemoryStream memoryStream = await file.CreateMemoryStreamAsync(4096))
{
// Sync calls are ok once file is in memory
memoryStream.Read(buffer);
docManager.WriteImageToFile(memoryStream, upLoadFileName, Season);
}
}
}
How can I get the local file name in code?
You can get it from the FileInfo using the following code inside your foreach-loop:
IFileInfo fileInfo = await file.ReadFileInfoAsync();
string fileName = fileInfo.Name;
string contentType = fileInfo.Type;

how to read excel file in memory (without saving it in disk) and return its content dotnet core

Im working on a webApi using dotnet core that takes the excel file from IFormFile and reads its content.Iam following the article
https://levelup.gitconnected.com/reading-an-excel-file-using-an-asp-net-core-mvc-application-2693545577db which is doing the same thing except that the file here is present on the server and mine will be provided by user.
here is the code:
public IActionResult Test(IFormFile file)
{
List<UserModel> users = new List<UserModel>();
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = System.IO.File.Open(file.FileName, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
while (reader.Read()) //Each row of the file
{
users.Add(new UserModel
{
Name = reader.GetValue(0).ToString(),
Email = reader.GetValue(1).ToString(),
Phone = reader.GetValue(2).ToString()
});
}
}
}
return Ok(users);
}
}
When system.IO tries to open the file, it could not find the path as the path is not present. How it is possible to either get the file path (that would vary based on user selection of file)? are there any other ways to make it possible.
PS: I dont want to upload the file on the server first, then read it.
You're using the file.FileName property, which refers to the file name the browser send. It's good to know, but not a real file on the server yet. You have to use the CopyTo(Stream) Method to access the data:
public IActionResult Test(IFormFile file)
{
List<UserModel> users = new List<UserModel>();
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = new MemoryStream())
{
file.CopyTo(stream);
stream.Position = 0;
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
while (reader.Read()) //Each row of the file
{
users.Add(new UserModel{Name = reader.GetValue(0).ToString(), Email = reader.GetValue(1).ToString(), Phone = reader.GetValue(2).ToString()});
}
}
}
return Ok(users);
}
Reference

Sensenet: upload files through sensenet client API for version 6.5 is not working properly

I have installed SenseNet version 6.5 (Code from codeplex). Wanted to upload the files in content repositry using Sensenet Client API, unfortunately it is not working with bulk upload.
string [] fileEntries = Directory.GetFiles(#"C:\Users\conyna\Downloads\Chirag");
foreach (string fileName in fileEntries)
{
using (Stream fs = File.OpenRead(fileName))
{
string fn = Path.GetFileName(fileName);
Task<SenseNet.Client.Content> x = SenseNet.Client.Content.UploadAsync("/Root/Sites/Default_Site/workspaces/(apps)/DocumentLibrary", fn, fs);
}
}
There are two problems with the code above:
you have to 'await' for async methods. Currently you start the task with the UploadAsync method, but you do not wait for it to finish, which casuses problems, because the file stream closes immediately after starting the upload task. Please upload files in an async way (of course you'll have to make your caller method async too, but that is the point of using an async api):
await Content.UploadAsync(...)
You may also consider using the Importer class in the client, it is able to import full directory structures.
You are trying to upload into an (apps) folder, which is not a correct target, that was designed to contain applications (mostly pages). It would be better if you uploaded into a document library in a workspace, for example:
/Root/Sites/Default_Site/workspaces/Document/SampleWorkspace/DocumentLibrary
We created a small application with SN ClientLibrary. I think, you can use this application/information/code.
This application can upload entire folders via Client Libray. Please check it out my Github repository: https://github.com/marosvolgyiz/SNClientLibraryUploader
There is relevant upload method:
public async Task Upload()
{
try
{
Console.WriteLine("Initilization...");
ClientContext.Initialize(new[] { sctx });
Console.WriteLine("Upload Started");
//Is Parent exists
var content = await Content.LoadAsync(Target);
if (content != null)
{
//Uploading files
var tasks = new List<Task>();
foreach (var file in Files)
{
string fileTargetFolder = Target + file.DirectoryName.Replace(Source, "").Replace(BaseDirectory, "").Replace("\\", "/");
var fileTargetContentFolder = await Content.LoadAsync(fileTargetFolder);
if (fileTargetContentFolder == null)
{
if (CreateFolderPath(Target, file.DirectoryName.Replace(Source, "")))
{
fileTargetContentFolder = await Content.LoadAsync(fileTargetFolder);
Console.WriteLine("#Upload file: " + file.FullName);
tasks.Add(Content.UploadAsync(fileTargetContentFolder.Id, file.Name, file.OpenRead()));
LoggerClass.LogToCSV("File uploaded", file.Name);
}
else
{
LoggerClass.LogToCSV("File target folder does not exist or you do not have enough permission to see! File can not be uploaded. ", file.Name);
}
}
else
{
Console.WriteLine("#Upload file: " + file.FullName);
tasks.Add(Content.UploadAsync(fileTargetContentFolder.Id, file.Name, file.OpenRead()));
LoggerClass.LogToCSV("File uploaded", file.Name);
}
}
await Task.WhenAll(tasks);
}
else
{
Console.WriteLine("Target does not exist or you do not have enough permission to see!");
LoggerClass.LogToCSV("Target does not exist or you do not have enough permission to see!");
}
Console.WriteLine("Upload finished.");
}
catch (Exception ex)
{
LoggerClass.LogToCSV(ex.Message);
}
}
I hope my answer is helpful to you.
Br,
maros

XmlException only during high usage DataContractSerializer

I have a working windows 8 caching solution using DataContractSerializer that raises a XmlException "Unexpected end of file" only when the UI is being used 'quickly'.
public static class CachingData<T>
{
public static async void Save(T data, string filename, StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
try
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream raStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream outStream = raStream.GetOutputStreamAt(0))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(outStream.AsStreamForWrite(), data);
await outStream.FlushAsync();
}
}
}
catch (Exception exc)
{
throw exc;
}
}
public static async System.Threading.Tasks.Task<T> Load(string filename, StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
T data = default(T);
StorageFile file = await folder.GetFileAsync(filename);
using (IInputStream inStream = await file.OpenSequentialReadAsync())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
data = (T)serializer.ReadObject(inStream.AsStreamForRead());
}
return data;
}
}
e.g. user clicks on item in list CachingData.Load is called async via await, checks for FileNotEoundException and either loads the data from disk or from the network, serialising on completion.
After first loaded user selects another item in the list and cycle repeats.
The problem occurs when "After first loaded" becomes "does not wait for load" and the item selected is not available cached.
Not quite sure how to proceed or even how to debug, hoping that just ignoring will allow the app to continue(just withough the nice speed increase of caching)