0

I am looking to read folders and files from a directory structure like so.. e.g

C:\RootFolder
            SubFolder1
               SubFolder2
                   File1
                   File2
                         SubFolder3
                                 File3
 Files....
 Files....

I would like to read both, files and folders and write to another directory I cant use copy , because the directory I want to write to is remote and not local.

I read the files here.... Id love to be able to read folders and files and write both to another directory.

 public static IEnumerable<FileInfo> GetFiles(string dir)
        {
            return Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories)
                .Select(path =>
                {                    
                    var stream = File.OpenRead(path);
                    return new FileInfo(Path.GetFileName(path), stream);
                })
                .DisposeEach(c => c.Content);
        }

this function writes files to a remote sftp site.

 public Task Write(IEnumerable<FileInfo> files)
        {
            return Task.Run(() =>
            {
                using (var sftp = new SftpClient(this.sshInfo))
                {
                    sftp.Connect();
                    sftp.ChangeDirectory(this.remoteDirectory);

                    foreach (var file in files)
                    {
                      sftp.UploadFile(file.Content, file.RelativePath);
                    }
                }
            });
        }

In this function I write the read files from the above function.

    private async static Task SendBatch(Config config, Batch batch, IRemoteFileWriter writer)
    {
        var sendingDir = GetClientSendingDirectory(config, batch.ClientName);
        Directory.CreateDirectory(Path.GetDirectoryName(sendingDir));
        Directory.Move(batch.LocalDirectory, sendingDir);
        Directory.CreateDirectory(batch.LocalDirectory);
        //Use RemoteFileWriter...
        var files = GetFiles(sendingDir);

        await writer.Write(files).ContinueWith(t =>
        {
            if(t.IsCompleted)
            {
                var zipArchivePath = GetArchiveDirectory(config, batch);
                ZipFile.CreateFromDirectory(
                    sendingDir,
                    zipArchivePath + " " +
                    DateTime.Now.ToString("yyyy-MM-dd hh mm ss.Zip")
                    );
            }
        });



    }

Thank you!

Ahhzeee
  • 123
  • 1
  • 12
  • Why would anyone thumb down this question ? Is it not a legit question or is there something wrong with the question.... I would like to know for future reference. Thank you – Ahhzeee Jan 20 '18 at 03:42
  • Is English not your primary language? Your phrasing is awkward. "After I successfully read the files , which I cant do unless I either specifically choose to" What? –  Jan 20 '18 at 03:54
  • 1
    What I'm saying is, your English is hard to understand. I suspect that is why you are downvoted. I would try to clarify that sentence I quoted. –  Jan 20 '18 at 03:58
  • I thought the sentence that followed explained choose to " read files without the folders or read from folders only if I combine either I get "Unauthorized Exception" does this not explain my choose to.... ? – Ahhzeee Jan 20 '18 at 03:59
  • Your edit is much better. –  Jan 20 '18 at 04:43
  • Thanks! for the feedback – Ahhzeee Jan 20 '18 at 04:52

2 Answers2

1

You are getting UnauthorizedAccessException: Access to the path 'C:\temp' is denied. because you can't open a stream from a folder as it doesn't contain bytes.

From what I can understand you are looking to copy the files from one folder to another.

This answer seems to cover what you are doing. https://stackoverflow.com/a/3822913/3634581

Once you have copied the directories you can then create the zip file.

If you don't need to copy the files and just create the Zip I would recommend that since it will reduce the disk IO and speed up the process.

ZipArchive (https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx) can be used to create a zip file straight to a stream.

Andrew McC
  • 98
  • 1
  • 5
  • Thank you for the answer but I need to write not copy. I need to write the files to a sftp remote site, this is why I need to read the files in the exact format or structure they are in. I will add the the Write function to help clarify this process. – Ahhzeee Jan 20 '18 at 03:47
  • 1
    I ran the Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories) function and it just returned files for me. Do you need to create empty folders? If so I would say you will need to have two methods one that creates folders and one that uploads the files. – Andrew McC Jan 20 '18 at 05:10
  • Yeah its exactly what I want to do, but the hurdle is how can I correlate the files and folders. Example; If I make folder A how can I make sure File A is inside, it gets even more complicated because I have folders inside sub folders inside sub folders. I did find one helpful method Path.GetRelativePath(string relative to, string path) hopefully I figure it soon when I do I will share the answer :) Thanks again!! – Ahhzeee Jan 23 '18 at 01:21
  • 1
    If you look at the 2nd answer on the question I linked to you can see how to traverse a path recursively. I think this would be all that is required to ensure you can replicate the directory and files the way you are looking for. https://stackoverflow.com/a/58779/3634581 Instead of calling file.CopyTo you can upload the file and at the top of the function you can create the directory. – Andrew McC Jan 23 '18 at 01:45
  • Thanks again and this did give me an idea but the sftp library did not have such methods, like the ones in .Net I.O library. – Ahhzeee Jan 24 '18 at 21:20
0

I figured it out here is the solution

 public Task Write(IEnumerable<FileInfo> files)
    {
        return Task.Run(() =>
        {
            using (var sftp = new SftpClient(this.sshInfo))
            {
                sftp.Connect();
                sftp.ChangeDirectory(this.remoteDirectory);

                foreach (var file in files)
                {
                    var parts = Path.GetDirectoryName(file.RelativePath)
                        .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);

                    sftp.ChangeDirectory(this.remoteDirectory);

                    foreach (var p in parts)
                    {
                        try
                        {
                            sftp.ChangeDirectory(p);
                        }
                        catch (SftpPathNotFoundException)
                        {
                            sftp.CreateDirectory(p);
                            sftp.ChangeDirectory(p);
                        }
                    }

                    sftp.UploadFile(file.Content, Path.GetFileName(file.RelativePath));
                }
            }
        });
    }

****Key point to the solution was this

var parts = Path.GetDirectoryName(file.RelativePath)
                            .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);

We call *Path.GetDirectoryName on the file itself to get the directory that correlates to the file. We split the file directory to get the folder name and finally create the folder name we obtained from the split and upload the file to it.

I hope this helps others who may encounter such issue.

Ahhzeee
  • 123
  • 1
  • 12