简体   繁体   中英

Append (n) to file names (not using system.io.file) to ensure unique names

I have a class that holds the name of a file, and the data of a file:

public class FileMeta
{
    public string FileName { get; set; }
    public byte[] FileData { get; set; }
}

I have a method that populates a collection of this class through an async download operation ( Files are not coming from a local file system ):

async Task<List<FileMeta>> ReturnFileData(IEnumerable<string> urls)
{
    using (var client = new HttpClient())
    {
        var results = await Task.WhenAll(urls.Select(async url => new
        {
            FileName = Path.GetFileName(url),
            FileData = await client.GetByteArrayAsync(url),
        }));
        return results.Select(result => 
            new FileMeta
            {
                FileName = result.FileName, 
                FileData = result.FileData
            }).ToList();
    }
}

I am going to feed this List<FileMeta> into a ZipFile creator, and the ZipFile like all File containers needs unique file names.

Readability is important, and I would like to be able to do the following:

file.txt => file.txt
file.txt => file(1).txt
file.txt => file(2).txt

There are a number of examples on how to do this within the file system, but not with a simple object collection. (Using System.IO.File.Exists for example)

What's the best way to loop through this collection of objects and return a unique set of file names?

How far I've gotten

private List<FileMeta> EnsureUniqueFileNames(IEnumerable<FileMeta> fileMetas)
{
    var returnList = new List<FileMeta>();
    foreach (var file in fileMetas)
    {
        while (DoesFileNameExist(file.FileName, returnList))
        {
             //Append (n) in sequence until match is not found?
        }
    }
    return returnList;
}

private bool DoesFileNameExist(string fileName, IEnumerable<FileMeta> fileMeta)
{
    var fileNames = fileMeta.Select(file => file.FileName).ToList();
    return fileNames.Contains(fileName);
}

You can try the following to increment the filenames:

private List<FileMeta> EnsureUniqueFileNames(IEnumerable<FileMeta> fileMetas)
{
    var returnedList = new List<FileMeta>();
    foreach (var file in fileMetas)
    {
        int count = 0;
        string originalFileName = file.FileName;
        while (returnedList.Any(fileMeta => fileMeta.FileName.Equals(file.FileName, 
                                            StringComparison.OrdinalIgnoreCase))
        {
            string fileNameOnly = Path.GetFileNameWithoutExtension(originalFileName);
            string extension = Path.GetExtension(file.FileName);
            file.FileName = string.Format("{0}({1}){2}", fileNameOnly, count, extension);
            count++;
        }

        returnList.Add(file);
    }
    return returnList;
}

As a side note, in your ReturnFileData , you're generating two lists, one of anonymous type and one of your actual FileMeta type. You can reduce the creation of the intermediate list. Actually, you don't need to await inside the method at all:

private Task<FileMeta[]> ReturnFileDataAsync(IEnumerable<string> urls)
{
    var client = new HttpClient();
    return Task.WhenAll(urls.Select(async url => new FileMeta
    {
        FileName = Path.GetFileName(url),
        FileData = await client.GetByteArrayAsync(url),
    }));
}

I made the return type a FileMeta[] instead of a List<FileMeta> , as it is a fixed sized returning anyway, and reduces the need to call ToList on the returned array. I also added the Async postfix, to follow the TAP guidelines.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM