2
votes

I have an Azure Function (HttpTrigger) which creates a tar.gz file in my Azure Storage.

When I trigger it one time, the file is succesfully created. but if I hit it again I have an error:

[Info] The process cannot access the file 'D:\local\Temp\myDir\tarArchive.tar.gz' because it is being used by another process.

I have to restart the function in the Azure Portal to be able to create another file.

here is my code:

FileStream fs = new FileStream(myDir+"/firstFile", FileMode.Create);
fs.Write(bytesToCompress, 0, bytesToCompress.Length);
fs.Dispose();
FileStream sfs = new FileStream(myDir + "/secondfile", FileMode.Create);
sfs.Dispose();

DirectoryInfo DirectoryOfFilesToBeTarred = new DirectoryInfo(myDir);
FileInfo[] filesInDirectory = DirectoryOfFilesToBeTarred.GetFiles();

string tarArchiveName = myDir + "/tarArchive.tar.gz";

using (Stream targetStream = new GZipOutputStream(File.Create(tarArchiveName)))
 {
    using (TarArchive tarArchive = TarArchive.CreateOutputTarArchive(targetStream, TarBuffer.DefaultBlockFactor))
    {
        foreach(FileInfo fileToBeTarred in filesInDirectory)
        {
           log.Info(fileToBeTarred.FullName);
           TarEntry entry = TarEntry.CreateEntryFromFile(fileToBeTarred.FullName);                            
           tarArchive.WriteEntry(entry, true); // Error thrown here

        }
    }
 }

I thought fileToBeTarred was still in use when the function is called again (am I wrong?) but I've tried to create a stream from this FileInfo in order to Dispose() it, but didn't fix my problem. I've also tried to Delete() it, without any effect.

Is someone seeing what I don't see ?

Thanks for your help

UPDATE

Here is the corected code given by Wim Coenen

using (Stream fileStream = File.Create(tarArchiveName))
using (Stream targetStream = new GZipOutputStream(fileStream))
using (TarArchive tarArchive = TarArchive.CreateOutputTarArchive(targetStream, TarBuffer.DefaultBlockFactor))
 {
   foreach (FileInfo fileToBeTarred in filesInDirectory)
   {
     log.Info(fileToBeTarred.FullName);
     TarEntry entry = TarEntry.CreateEntryFromFile(fileToBeTarred.FullName);
     tarArchive.WriteEntry(entry, true); // Error thrown here 
   }
}

And the log of the error (e = Exception object) e.message =>

2018-08-01T11:59:46.887 [Info] The process cannot access the file 'D:\local\Temp\myDir\tarArchive.tar.gz' because it is being used by another process.

e.ToString() =>

2018-08-01T11:59:47.152 [Info] System.IO.IOException: The process cannot access the file 'D:\local\Temp\myDir\tarArchive.tar.gz' because it is being used by another process.

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)

at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)

at ICSharpCode.SharpZipLib.Tar.TarArchive.WriteEntryCore(TarEntry sourceEntry, Boolean recurse)

at ICSharpCode.SharpZipLib.Tar.TarArchive.WriteEntry(TarEntry sourceEntry, Boolean recurse)

at UploadFileFromBCText.Function1.d__4.MoveNext()

3

3 Answers

1
votes

It looks like you are not creating it in Azure Storage, but on the local disk of a Function App instance. That's probably not a good idea, since instances are short-lived and won't preserve your files for long.

Instead, have a look at Azure Blob Storage Output binding - its purpose is to store file in Azure Storage without using low-level File API or SDKs.

0
votes

The problem is that you are not disposing the stream returned by File.Create(tarArchiveName)). Fixed version:

using (Stream fileStream = File.Create(tarArchiveName))
using (Stream targetStream = new GZipOutputStream(fileStream))
using (TarArchive tarArchive = TarArchive.CreateOutputTarArchive(targetStream, TarBuffer.DefaultBlockFactor))
{
    foreach(FileInfo fileToBeTarred in filesInDirectory)
    {
        log.Info(fileToBeTarred.FullName);
        TarEntry entry = TarEntry.CreateEntryFromFile(fileToBeTarred.FullName);                            
        tarArchive.WriteEntry(entry, true);
    }
 }
0
votes

I've just find what was the problem:

In fact when I hit for the first time the trigger, myDir is empty. So I create at this time the two files

FileStream fs = new FileStream(myDir+"/firstFile", FileMode.Create);
fs.Write(bytesToCompress, 0, bytesToCompress.Length);
fs.Dispose();
FileStream sfs = new FileStream(myDir + "/secondfile", FileMode.Create);
sfs.Dispose();

With these files, I create a tar.gz archive with the files in the directory, which doesn't exists yet.

The second time I hit the trigger the tar.gz file exists, and belongs to the directory. So when I do

foreach(FileInfo fileToBeTarred in filesInDirectory)
    {
        log.Info(fileToBeTarred.FullName);
        TarEntry entry = TarEntry.CreateEntryFromFile(fileToBeTarred.FullName);                            
        tarArchive.WriteEntry(entry, true);
    }

At a moment, it works with the tar.gz file ..!

filesInDiretory is filled by this method:

FileInfo[] filesInDirectory = DirectoryOfFilesToBeTarred.GetFiles();

So I solve this issue by:

  • Checking if the file exists. If it is, I delete it.

  • Fill filesInDirectory AFTER

So my complete code looks like this now:

        DirectoryInfo DirectoryOfFilesToBeTarred = new DirectoryInfo(myDir);
        string tarArchiveName = myDir + "/tarArchive.tar.gz";
        if (File.Exists(tarArchiveName))
        {
            log.Info("file exists");
            File.Delete(tarArchiveName);

        }
        FileInfo[] filesInDirectory = DirectoryOfFilesToBeTarred.GetFiles();

        using (Stream fileStream = File.Create(tarArchiveName))
        using (Stream targetStream = new GZipOutputStream(fileStream))
        using (TarArchive tarArchive = TarArchive.CreateOutputTarArchive(targetStream, TarBuffer.DefaultBlockFactor))
        {
            foreach (FileInfo fileToBeTarred in filesInDirectory)
            {
                Directory.SetCurrentDirectory(Path.GetTempPath() + "/myDir");
                log.Info(fileToBeTarred.Name);
                log.Info(fileToBeTarred.FullName);
                TarEntry entry = TarEntry.CreateEntryFromFile(fileToBeTarred.FullName);

                tarArchive.WriteEntry(entry, true);


            }

        }