Skip to content

Commit

Permalink
Merge pull request #130 from EasyAbp/allows-case-diff-only-renaming
Browse files Browse the repository at this point in the history
Allows file renaming with only case differences
  • Loading branch information
gdlcf88 committed Apr 18, 2024
2 parents c44db64 + b020412 commit 0499e3b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 27 deletions.
2 changes: 1 addition & 1 deletion common.props
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>5.2.0</Version>
<Version>5.3.0-preview.1</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>EasyAbp Team</Authors>
Expand Down
Expand Up @@ -67,7 +67,7 @@ public virtual async Task<File> FindByPathAsync(string path, string fileContaine

foreach (var fileName in splitFileName)
{
foundFile = await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId);
foundFile = await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId, false);

if (foundFile is null)
{
Expand Down Expand Up @@ -131,7 +131,7 @@ public virtual async Task<FileDownloadInfoModel> GetDownloadInfoAsync(File file)
if (model.NewFileName != file.FileName)
{
await CheckFileNotExistAsync(model.NewFileName, file.ParentId, file.FileContainerName,
file.OwnerUserId);
file.OwnerUserId, true);
}

file.UpdateInfo(model.NewFileName, model.NewMimeType, file.SubFilesQuantity, file.ByteSize, file.Hash,
Expand Down Expand Up @@ -165,7 +165,7 @@ public virtual async Task<FileDownloadInfoModel> GetDownloadInfoAsync(File file)
if (model.NewFileName != file.FileName || newParent?.Id != oldParent?.Id)
{
await CheckFileNotExistAsync(model.NewFileName, newParent?.Id, file.FileContainerName,
file.OwnerUserId);
file.OwnerUserId, true);
}

if (oldParent != newParent)
Expand Down Expand Up @@ -296,15 +296,15 @@ protected virtual void CheckFileName(string fileName, IFileContainerConfiguratio

[UnitOfWork]
protected virtual async Task<bool> IsFileExistAsync(string fileName, Guid? parentId, string fileContainerName,
Guid? ownerUserId)
Guid? ownerUserId, bool forceCaseSensitive)
{
return await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId) != null;
return await FileRepository.ExistAsync(fileName, parentId, fileContainerName, ownerUserId, forceCaseSensitive);
}

protected virtual async Task CheckFileNotExistAsync(string fileName, Guid? parentId, string fileContainerName,
Guid? ownerUserId)
Guid? ownerUserId, bool forceCaseSensitive)
{
if (await IsFileExistAsync(fileName, parentId, fileContainerName, ownerUserId))
if (await IsFileExistAsync(fileName, parentId, fileContainerName, ownerUserId, forceCaseSensitive))
{
throw new FileAlreadyExistsException(fileName, parentId);
}
Expand Down
Expand Up @@ -10,9 +10,12 @@ public interface IFileRepository : IRepository<File, Guid>
{
Task<List<File>> GetListAsync(Guid? parentId, string fileContainerName, Guid? ownerUserId,
FileType? specifiedFileType = null, CancellationToken cancellationToken = default);

Task<File> FindAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId,
CancellationToken cancellationToken = default);
bool forceCaseSensitive, CancellationToken cancellationToken = default);

Task<bool> ExistAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId,
bool forceCaseSensitive, CancellationToken cancellationToken = default);

Task<File> FirstOrDefaultAsync(string fileContainerName, string hash, long byteSize,
CancellationToken cancellationToken = default);
Expand Down
Expand Up @@ -81,14 +81,15 @@ public class FileManager : FileManagerBase, IFileManager
if (configuration.EnableAutoRename)
{
if (await IsFileExistAsync(model.FileName, model.Parent?.Id, model.FileContainerName,
model.OwnerUserId))
model.OwnerUserId, false))
{
model.FileName = await FileRepository.GetFileNameWithNextSerialNumberAsync(model.FileName,
model.Parent?.Id, model.FileContainerName, model.OwnerUserId, cancellationToken);
}
}

await CheckFileNotExistAsync(model.FileName, model.Parent?.Id, model.FileContainerName, model.OwnerUserId);
await CheckFileNotExistAsync(
model.FileName, model.Parent?.Id, model.FileContainerName, model.OwnerUserId, false);

var file = new File(GuidGenerator.Create(), CurrentTenant.Id, model.Parent, model.FileContainerName,
model.FileName, model.MimeType, model.FileType, 0, model.GetContentLength(), hashString,
Expand Down
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
Expand All @@ -22,49 +21,86 @@ public FileRepository(IDbContextProvider<IFileManagementDbContext> dbContextProv
public virtual async Task<List<File>> GetListAsync(Guid? parentId, string fileContainerName, Guid? ownerUserId,
FileType? specifiedFileType = null, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
return await (await GetQueryableAsync())
.Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId &&
x.FileContainerName == fileContainerName).WhereIf(specifiedFileType.HasValue,
x => x.FileType == specifiedFileType.Value).ToListAsync(cancellationToken);
}

public virtual async Task<File> FindAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId,
CancellationToken cancellationToken = default)
public virtual async Task<File> FindAsync(string fileName, Guid? parentId, string fileContainerName,
Guid? ownerUserId, bool forceCaseSensitive, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId &&
x.FileContainerName == fileContainerName && x.FileName == fileName)
.FirstOrDefaultAsync(cancellationToken);
var queryable = await GetQueryableAsync();

if (forceCaseSensitive)
{
queryable = queryable.AsNoTracking().Where(x =>
x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName);

var files = await queryable.ToListAsync(cancellationToken);

var foundFile = files.FirstOrDefault(x => x.FileName == fileName);
return foundFile is null ? null : await GetAsync(foundFile.Id, cancellationToken: cancellationToken);
}

queryable = queryable.Where(x =>
x.ParentId == parentId && x.OwnerUserId == ownerUserId &&
x.FileContainerName == fileContainerName && x.FileName == fileName);

return await queryable.FirstOrDefaultAsync(cancellationToken);
}

public virtual async Task<bool> ExistAsync(string fileName, Guid? parentId, string fileContainerName,
Guid? ownerUserId, bool forceCaseSensitive, CancellationToken cancellationToken = default)
{
var queryable = await GetQueryableAsync();

if (forceCaseSensitive)
{
queryable = queryable.AsNoTracking().Where(x =>
x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName);

var files = await queryable.ToListAsync(cancellationToken);

return files.Any(x => x.FileName == fileName);
}

queryable = queryable.Where(x =>
x.ParentId == parentId && x.OwnerUserId == ownerUserId &&
x.FileContainerName == fileContainerName && x.FileName == fileName);

return await queryable.AnyAsync(cancellationToken);
}

public virtual async Task<File> FirstOrDefaultAsync(string fileContainerName, string hash, long byteSize,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
return await (await GetQueryableAsync())
.Where(x => x.Hash == hash && x.ByteSize == byteSize && x.FileContainerName == fileContainerName)
.FirstOrDefaultAsync(cancellationToken);
}

public virtual async Task<File> FirstOrDefaultAsync(string fileContainerName, string blobName,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync()).Where(x => x.BlobName == blobName && x.FileContainerName == fileContainerName)
return await (await GetQueryableAsync())
.Where(x => x.BlobName == blobName && x.FileContainerName == fileContainerName)
.FirstOrDefaultAsync(cancellationToken);
}

public virtual async Task<SubFilesStatisticDataModel> GetSubFilesStatisticDataAsync(Guid id,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync()).Where(x => x.ParentId == id).GroupBy(x => true).Select(x =>
return await (await GetQueryableAsync()).Where(x => x.ParentId == id).GroupBy(x => true).Select(x =>
new SubFilesStatisticDataModel
{
SubFilesQuantity = x.Count(),
ByteSize = x.Sum(y => y.ByteSize)
}).FirstOrDefaultAsync(cancellationToken) ?? new SubFilesStatisticDataModel();
}

public virtual async Task<string> GetFileNameWithNextSerialNumberAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId,
CancellationToken cancellationToken = default)
public virtual async Task<string> GetFileNameWithNextSerialNumberAsync(string fileName, Guid? parentId,
string fileContainerName, Guid? ownerUserId, CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(fileName, nameof(fileName));

Expand All @@ -76,14 +112,15 @@ public FileRepository(IDbContextProvider<IFileManagementDbContext> dbContextProv

var part2 = ')' + ext;

var fileNames = await (await GetDbSetAsync())
var fileNames = await (await GetQueryableAsync())
.Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId &&
x.FileContainerName == fileContainerName && x.FileName.StartsWith(part1) &&
x.FileName.EndsWith(part2)).Select(x => x.FileName).ToListAsync(cancellationToken);

var nextNumber =
fileNames
.Select(x => x.Substring(part1.Length, x.LastIndexOf(part2, StringComparison.Ordinal) - part1.Length))
.Select(x =>
x.Substring(part1.Length, x.LastIndexOf(part2, StringComparison.Ordinal) - part1.Length))
.Select(x => int.TryParse(x, out var number) ? number : 0).Where(x => x > 0).OrderBy(x => x)
.TakeWhile((x, i) => x == i + 1).LastOrDefault() + 1;

Expand Down
32 changes: 32 additions & 0 deletions test/EasyAbp.FileManagement.Domain.Tests/Files/FileDomainTests.cs
Expand Up @@ -209,4 +209,36 @@ public async Task Should_Get_File_By_Path()
(await FileManager.GetByPathAsync("dir/sub-dir", "test", null)).Id.ShouldBe(subDir.Id);
(await FileManager.GetByPathAsync("dir/sub-dir/file.txt", "test", null)).Id.ShouldBe(file.Id);
}

[Fact]
public async Task Should_Rename_From_abc_To_ABC()
{
var dir = await FileManager.CreateAsync(new CreateFileModel("test", null, "abc", null,
FileType.Directory, null, null));

await Should.NotThrowAsync(() =>
FileManager.UpdateInfoAsync(dir, new UpdateFileInfoModel("ABC", dir.MimeType)));

dir.FileName.ShouldBe("ABC");

dir = await FileRepository.GetAsync(dir.Id);

dir.FileName.ShouldBe("ABC");
}

[Fact]
public async Task Should_Rename_From_abc_To_abc()
{
var dir = await FileManager.CreateAsync(new CreateFileModel("test", null, "abc", null,
FileType.Directory, null, null));

await Should.NotThrowAsync(() =>
FileManager.UpdateInfoAsync(dir, new UpdateFileInfoModel("abc", dir.MimeType)));

dir.FileName.ShouldBe("abc");

dir = await FileRepository.GetAsync(dir.Id);

dir.FileName.ShouldBe("abc");
}
}

0 comments on commit 0499e3b

Please sign in to comment.