Why is my download from Azure storage empty? - c#

I can connect to the Azure Storage account and can even upload a file, but when I go to download the file using DownloadToFileAsync() I get a 0kb file as a result.
I have checked and the "CloudFileDirectory" and the "CloudFile" fields are all correct, which means the connection with Azure is solid. I can even write the output from the file to the console, but I cannot seem to save it as a file.
public static string PullFromAzureStorage(string azureFileConn, string remoteFileName, string clientID)
{
var localDirectory = #"C:\cod\clients\" + clientID + #"\ftp\";
var localFileName = clientID + "_xxx_" + remoteFileName;
//Retrieve storage account from connection string
var storageAccount = CloudStorageAccount.Parse(azureFileConn);
var client = storageAccount.CreateCloudFileClient();
var share = client.GetShareReference("testing");
// Get a reference to the root directory for the share
CloudFileDirectory rootDir = share.GetRootDirectoryReference();
//Get a ref to client folder
CloudFileDirectory cloudFileDirectory = rootDir.GetDirectoryReference(clientID);
// Get a reference to the directory we created previously
CloudFileDirectory unprocessed = cloudFileDirectory.GetDirectoryReference("Unprocessed");
// Get a reference to the file
CloudFile sourceFile = unprocessed.GetFileReference(remoteFileName);
//write to console and log
Console.WriteLine("Downloading file: " + remoteFileName);
LogWriter.LogWrite("Downloading file: " + remoteFileName);
//Console.WriteLine(sourceFile.DownloadTextAsync().Result);
sourceFile.DownloadToFileAsync(Path.Combine(localDirectory, localFileName), FileMode.Create);
//write to console and log
Console.WriteLine("Download Successful!");
LogWriter.LogWrite("Download Successful!");
//delete remote file after download
//sftp.DeleteFile(remoteDirectory + remoteFileName);
return localFileName;
}

In the commented out line of code where you write the output to the Console, you explicitly use .Result because you're calling an async method in a synchronous one. You should either also do so while downloading the file as well, or make the entire method around it async.
The first solution would look something like this:
sourceFile.DownloadToFileAsync(Path.Combine(localDirectory, localFileName), FileMode.Create).Result();
EDIT:
As far as the difference with the comment, that uses GetAwaiter().GetResult(), goes: .Result wraps any exception that might occur in an AggregateException, while GetAwaiter().GetResult() won't. Anyhow: if there's any possibility you can refactor the method to be async so you can use await: please do so.

Related

Unable to move directory in azure using ShareDirectoryClient

Normally we can move the directory using
// source is: "C:\Songs\Elvis my Man"
// newLocation is: "C:\Songs\Elvis"
try
{
// Previous command was: Directory.Move(source, newLocation);
DirectoryInfo dir = new DirectoryInfo(source);
dir.MoveTo(newLocation);
}
catch (Exception e)
{
Console.WriteLine("Error: "+ e.Message);
}
Now, when using the azure:
string myconnectionString = ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString.ToString();
string myshareName = "Mysampleshare";
string mydirName = "Mysampledir";
// Get a reference to a share and then create it
ShareClient myshare = new ShareClient(myconnectionString, myshareName);
ShareDirectoryClient directory1 = myshare.GetDirectoryClient(mydirName);
string myshareName2 = "Mysampleshare2";
string mydirName2 = "Mysampledir2";
// Get a reference to a share and then create it
ShareClient myshare2 = new ShareClient(myconnectionString, myshareName2);
ShareDirectoryClient directory2 = myshare2.GetDirectoryClient(mydirName2);
Directory.Move(directory1.Path, directory2.Path);
I am unable to move the directory from one location to another using azure. I am getting exception.
DirectoryNotFoundException: The path specified by sourceDirName is invalid
Please suggest your advice.
ShareDirectoryClient essentially implements Azure File Service REST API thus you cannot use System.IO operations like Directory.Move with it.
There are two possible solutions:
If you want to use the SDK, what you will need to do is list files and directories recursively in the source directory and then copy individual files from source directory to target directory. You will also need to create directories in the target directory as well. Once the copy operation is complete, then you will need to delete all files and directories from the source directory. Once the source directory is empty, only then you will be able to delete the source directory.
You will need to do all this because the REST API does not natively support move operation. To accomplish move, you will need to perform copy operation followed by delete operation.
If you want to use System.IO, then you will need to mount the file share as a shared network drive so that you can get a drive letter assigned to that file share. Once you have that, then you will be able to use operations like Directory.Move available in System.IO namespace.
UPDATE
Please try the code below:
using System;
using System.Threading.Tasks;
using Azure.Storage.Files.Shares;
namespace SO69798149
{
class Program
{
const string MyconnectionString = "DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key";
const string MyshareName = "share-name";
const string SourceDirectoryName = "source-directory-name";
private const string RenamedDirectoryName = "new-directory-name";
static async Task Main(string[] args)
{
ShareClient myshare = new ShareClient(MyconnectionString, MyshareName);
ShareDirectoryClient sourceDirectoryClient = myshare.GetDirectoryClient(SourceDirectoryName);
ShareDirectoryClient targetDirectoryClient = myshare.GetDirectoryClient(RenamedDirectoryName);
await RenameDirectory(sourceDirectoryClient, targetDirectoryClient);
Console.WriteLine("Directory renamed.");
}
static async Task RenameDirectory(ShareDirectoryClient sourceDirectoryClient,
ShareDirectoryClient targetDirectoryClient)
{
//Create target directory
await targetDirectoryClient.CreateIfNotExistsAsync();
//List files and folders from the source directory
var result = sourceDirectoryClient.GetFilesAndDirectoriesAsync();
await foreach (var items in result.AsPages())
{
foreach (var item in items.Values)
{
if (item.IsDirectory)
{
//If item is directory, then get the child items in that directory recursively.
await RenameDirectory(sourceDirectoryClient.GetSubdirectoryClient(item.Name),
targetDirectoryClient.GetSubdirectoryClient(item.Name));
}
else
{
//If item is file, then copy the file and then delete it.
var sourceFileClient = sourceDirectoryClient.GetFileClient(item.Name);
var targetFileClient = targetDirectoryClient.GetFileClient(item.Name);
await targetFileClient.StartCopyAsync(sourceFileClient.Uri);
await sourceFileClient.DeleteIfExistsAsync();
}
}
}
//Delete source directory.
await sourceDirectoryClient.DeleteIfExistsAsync();
}
}
}

How I can create file on Xamarin.ios (PHP Session)

i need to create a (.txt) file on my application root on Xamarin.ios, so a need to store in memory a String, contain a UserName, after the user logged in, I can use each String to run a "httprequest" to my mySql database online. Similar procedure to the sessions in PHP, but client side and in C#.. Thanks you. I just try, with cookie, but I need to access at the UserName in every class and in every storyBoard.
I just try to write a JSON file serializable:
String NU = "NomeUtente=" + NomeUtente.Text;
string json = JsonConvert.SerializeObject(NU, Formatting.Indented);
System.IO.File.WriteAllText(#"/558gt.txt", json);
but Xamarin.ios throw a exception:
SystemUnauthorizedAccessexception -->
access to the path /558gt.txt denied.
If you need store data, use Xamarin.Essentials.SecureStorage
Check documentation
Thanks you. I read the documents on the ios file system and managed to write the portion of code necessary for reading and writing the Session file containing the username.
try {
//creo il file nella sandbox maledetta di iOS
//create file Session
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Session.txt");
//filename contiene la path completa di dove sta il file
File.WriteAllText(filename, NomeUtente.Text);
}
catch (Exception) {
label1.Text = "Errore generico.";
}
and now I read the file, in a separated class and storyboard:
//Recupero il nomeutente
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Session.txt");
var NomeUtente = File.ReadAllText(filename); //contiene il NomeUtente

How to create an empty folder in S3 using C#? [duplicate]

I'm trying to determine if a folder exists on my Amazon S3 Bucket and if it doesn't I want to create it.
At the moment I can create the folder using the .NET SDK as follows:
public void CreateFolder(string bucketName, string folderName)
{
var folderKey = folderName + "/"; //end the folder name with "/"
var request = new PutObjectRequest();
request.WithBucketName(bucketName);
request.StorageClass = S3StorageClass.Standard;
request.ServerSideEncryptionMethod = ServerSideEncryptionMethod.None;
//request.CannedACL = S3CannedACL.BucketOwnerFullControl;
request.WithKey(folderKey);
request.WithContentBody(string.Empty);
S3Response response = m_S3Client.PutObject(request);
}
Now when I try to see if the folder exists using this code:
public bool DoesFolderExist(string key, string bucketName)
{
try
{
S3Response response = m_S3Client.GetObjectMetadata(new GetObjectMetadataRequest()
.WithBucketName(bucketName)
.WithKey(key));
return true;
}
catch (Amazon.S3.AmazonS3Exception ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
return false;
//status wasn't not found, so throw the exception
throw;
}
}
It cannot find the folder. The strange thing is if I create the folder using the AWS Management Console, the 'DoesFolderExist' method can see it.
I'm not sure if it's an ACL/IAM thing but am not sure how to resolve this.
Your code actually works for me, but there are a few things you need to be aware off.
As I understand it, Amazon S3 does not have a concept of folders, but individual clients may display the S3 objects as if they did. So if you create an object called A/B , then the client may display it as if it was an object called B inside a folder called A. This is intuitive and seems to have become a standard, but simulating an empty folder does not appear to have a standard.
For example, I used your method to create a folder called Test, then actually end up creating an object called Test/. But I created a folder called Test2 in AWS Explorer (ie the addon to Visual Studio) and it ended up creating an object called Test2/Test2_$folder$
(AWS Explorer will display both Test and Test2 as folders)
Once of the things that this means is that you don't need to create the 'folder' before you can use it, which may mean that you don't need a DoesFolderExist method.
As I mention I tried your code and it works and finds the Test folder it created, but the key had to be tweaked to find the folder created by AWS Explorer , ie
DoesFolderExist("Test/" , bucketName); // Returns true
DoesFolderExist("Test2/" , bucketName); // Returns false
DoesFolderExist("Test2/Test2_$folder$", bucketName); // Returns true
So if you do still want to have a DoesFolderExist method, then it might be safer to just look for any objects that start with folderName + "/" , ie something like
ListObjectsRequest request = new ListObjectsRequest();
request.BucketName = bucketName ;
request.WithPrefix(folderName + "/");
request.MaxKeys = 1;
using (ListObjectsResponse response = m_S3Client.ListObjects(request))
{
return (response.S3Objects.Count > 0);
}
Just refactored above codes to on async method with version 2 of AWS .Net SDK:
public async Task CreateFoldersAsync(string bucketName, string path)
{
path = path.EnsureEndsWith('/');
IAmazonS3 client =
new AmazonS3Client(YOUR.AccessKeyId, YOUR.SecretAccessKey,
RegionEndpoint.EUWest1);
var findFolderRequest = new ListObjectsV2Request();
findFolderRequest.BucketName = bucketName;
findFolderRequest.Prefix = path;
findFolderRequest.MaxKeys = 1;
ListObjectsV2Response findFolderResponse =
await client.ListObjectsV2Async(findFolderRequest);
if (findFolderResponse.S3Objects.Any())
{
return;
}
PutObjectRequest request = new PutObjectRequest()
{
BucketName = bucketName,
StorageClass = S3StorageClass.Standard,
ServerSideEncryptionMethod = ServerSideEncryptionMethod.None,
Key = path,
ContentBody = string.Empty
};
// add try catch in case you have exceptions shield/handling here
PutObjectResponse response = await client.PutObjectAsync(request);
}
ListObjectsRequest findFolderRequest = new ListObjectsRequest();
findFolderRequest.BucketName = bucketName;
findFolderRequest.Prefix = path;
ListObjectsResponse findFolderResponse = s3Client.ListObjects(findFolderRequest);
Boolean folderExists = findFolderResponse.S3Objects.Any();
path can be something like "images/40/".
Using the above code can check if a so-called folder "images/40/" under bucket exists or not.
But Amazon S3 data model does not have the concept of folders. When you try to copy a image or file to certain path, if this co-called folder does not exist it will be created automatically as part of key name of this file or image. Therefore, you actually do not need to check if this folder exists or not.
Very important information from docs.aws.amazon.com : The Amazon S3 data model is a flat structure: you create a bucket, and the bucket stores objects. There is no hierarchy of subbuckets or subfolders; however, you can infer logical hierarchy using key name prefixes and delimiters as the Amazon S3 console does.
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html

C# Windows Service Not Compressing Folder Correctly

Im currently building a Windows service that will be used to create backups of logs. Currently, the logs are stored at the path E:\Logs and intent is to copy the contents, timestamp their new folder and compress it. After this, you should have E:\Logs and E:\Logs_[Timestamp].zip. The zip will be moved to C:\Backups\ for later processing. Currently, I am using the following to try and zip the log folder:
var logDirectory = "E://Logs";
var timeStamp = DateTime.Now.ToString("yyyyMMddHHmm");
var zippedFolder = logDirectory + "_" + timeStamp + ".zip";
System.IO.Compression.ZipFile.CreateFromDirectory(logDirectory, zippedFolder);
While this appears to create a zip folder, I get the error Windows cannot open the folder. The Compressed (zipped) Folder E:\Logs_201805161035.zip is invalid.
To address any troubleshooting issues, the service is running with an AD account that has a sufficient permission level to perform administrative tasks. Another thing to consider is that the service kicks off when its FileSystemWatcher detects a new zip folder in the path C:\Aggregate. Since there are many zip folders that are added to C:\Aggregate at once, the FileSystemWatcher creates a new Task for each zip found. You can see how this works in the following:
private void FileFoundInDrops(object sender, FileSystemEventArgs e)
{
var aggregatePath = new DirectoryInfo("C://Aggregate");
if (e.FullPath.Contains(".zip"))
{
Task task = Task.Factory.StartNew(() =>
{
try
{
var logDirectory = "E://Logs";
var timeStamp = DateTime.Now.ToString("yyyyMMddHHmm");
var zippedFolder = logDirectory + "_" + timeStamp + ".zip";
ZipFile.CreateFromDirectory(logDirectory, zippedFolder);
}
catch (Exception ex)
{
Log.WriteLine(System.DateTime.Now.ToString() + " - ERROR: " + ex);
}
});
task.Dispose();
}
}
How can I get around the error I am receiving? Any help would be appreciated!

How to check a folder exists in DropBox using DropNet

I'm programming an app that interact with dropbox by use DropNet API. I want to check if the folder is exist or not on dropbox in order to I will create one and upload file on it after that. Everything seen fine but if my folder is exist it throw exception. Like this:
if (isAccessToken)
{
byte[] bytes = File.ReadAllBytes(fileName);
try
{
string dropboxFolder = "/Public/DropboxManagement/Logs" + folder;
// I want to check if the dropboxFolder is exist here
_client.CreateFolder(dropboxFolder);
var upload = _client.UploadFile(dropboxFolder, fileName, bytes);
}
catch (DropNet.Exceptions.DropboxException ex) {
MessageBox.Show(ex.Response.Content);
}
}
I'm not familiar with dropnet, but looking at the source code, it appears you should be able to do this by using the GetMetaData() method off of your _client object. This method returns a MetaData object.
Example:
//gets contents at requested path
var metaData = _client.GetMetaData("/Public/DropboxManagement/Logs");
//without knowing how this API works, Path may be a full path and therefore need to check for "/Public/DropboxManagement/Logs" + folder
if (metaData.Contents.Any(c => c.Is_Dir && c.Path == folder)
{
//folder exists
}

Categories