forked from ddrilling/AsbCloudServer
#6539681 file storage repository + tests
This commit is contained in:
parent
f30e2737e7
commit
b6ce4cad77
@ -13,4 +13,12 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Data\DailyReport\" />
|
<None Remove="Data\DailyReport\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EditorConfigFiles Remove="D:\Source\AsbCloudApp\Services\.editorconfig" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="D:\Source\AsbCloudApp\Services\.editorconfig" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -14,11 +14,6 @@ namespace AsbCloudApp.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IFileService
|
public interface IFileService
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Директория хранения файлов
|
|
||||||
/// </summary>
|
|
||||||
string RootPath { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сохранить файл
|
/// Сохранить файл
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
51
AsbCloudApp/Services/IFileStorageRepository.cs
Normal file
51
AsbCloudApp/Services/IFileStorageRepository.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Репозиторий хранения фалов
|
||||||
|
/// </summary>
|
||||||
|
public interface IFileStorageRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Директория хранения файлов
|
||||||
|
/// </summary>
|
||||||
|
string RootPath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение длинны фала и проверка его наличия, если отсутствует падает исключение
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcFilePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
long GetLengthFile(string srcFilePath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Перемещение файла
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcFilePath"></param>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
void MoveFile (string srcFilePath, string filePath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Копирование файла
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удаление файла
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
void DeleteFile(string fileName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверка наличия файла
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullPath"></param>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool FileExists(string fullPath, string fileName);
|
||||||
|
}
|
||||||
|
}
|
@ -155,6 +155,7 @@ namespace AsbCloudInfrastructure
|
|||||||
.Include(c => c.Wells)
|
.Include(c => c.Wells)
|
||||||
.Include(c => c.Deposit))); // может быть включен в сервис ClusterService
|
.Include(c => c.Deposit))); // может быть включен в сервис ClusterService
|
||||||
services.AddTransient<IFileRepository, FileRepository>();
|
services.AddTransient<IFileRepository, FileRepository>();
|
||||||
|
services.AddTransient<IFileStorageRepository, FileStorageRepository>();
|
||||||
// Subsystem service
|
// Subsystem service
|
||||||
services.AddTransient<ICrudService<SubsystemDto>, CrudCacheServiceBase<SubsystemDto, Subsystem>>();
|
services.AddTransient<ICrudService<SubsystemDto>, CrudCacheServiceBase<SubsystemDto, Subsystem>>();
|
||||||
services.AddTransient<ISubsystemService, SubsystemService>();
|
services.AddTransient<ISubsystemService, SubsystemService>();
|
||||||
|
59
AsbCloudInfrastructure/Repository/FileStorageRepository.cs
Normal file
59
AsbCloudInfrastructure/Repository/FileStorageRepository.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using AsbCloudApp.Exceptions;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Repository
|
||||||
|
{
|
||||||
|
public class FileStorageRepository : IFileStorageRepository
|
||||||
|
{
|
||||||
|
public string RootPath { get; private set; }
|
||||||
|
|
||||||
|
public FileStorageRepository()
|
||||||
|
{
|
||||||
|
RootPath = "files";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token)
|
||||||
|
{
|
||||||
|
CreateDirectory(filePath);
|
||||||
|
using var newfileStream = new FileStream(filePath, FileMode.Create);
|
||||||
|
await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteFile(string fileName)
|
||||||
|
{
|
||||||
|
if (File.Exists(fileName))
|
||||||
|
File.Delete(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetLengthFile(string srcFilePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(srcFilePath))
|
||||||
|
throw new ArgumentInvalidException($"file {srcFilePath} doesn't exist", nameof(srcFilePath));
|
||||||
|
|
||||||
|
var sysFileInfo = new FileInfo(srcFilePath);
|
||||||
|
return sysFileInfo.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MoveFile(string srcFilePath, string filePath)
|
||||||
|
{
|
||||||
|
CreateDirectory(filePath);
|
||||||
|
File.Move(srcFilePath, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string fullPath, string fileName)
|
||||||
|
{
|
||||||
|
if (!File.Exists(fullPath))
|
||||||
|
throw new FileNotFoundException("not found", fileName);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDirectory(string filePath)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -481,7 +481,8 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
|||||||
.Options;
|
.Options;
|
||||||
using var context = new AsbCloudDbContext(contextOptions);
|
using var context = new AsbCloudDbContext(contextOptions);
|
||||||
var fileRepository = new FileRepository(context);
|
var fileRepository = new FileRepository(context);
|
||||||
var fileService = new FileService(fileRepository);
|
var fileStorageRepository = new FileStorageRepository();
|
||||||
|
var fileService = new FileService(fileRepository, fileStorageRepository);
|
||||||
var files = state.Parts.Select(p => fileService.GetUrl(p.File));
|
var files = state.Parts.Select(p => fileService.GetUrl(p.File));
|
||||||
DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well);
|
DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well);
|
||||||
await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token);
|
await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token);
|
||||||
|
@ -16,13 +16,13 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
{
|
{
|
||||||
public class FileService : IFileService
|
public class FileService : IFileService
|
||||||
{
|
{
|
||||||
public string RootPath { get; private set; }
|
|
||||||
private readonly IFileRepository fileRepository;
|
private readonly IFileRepository fileRepository;
|
||||||
|
private readonly IFileStorageRepository fileStorageRepository;
|
||||||
|
|
||||||
public FileService(IFileRepository fileRepository)
|
public FileService(IFileRepository fileRepository, IFileStorageRepository fileStorageRepository)
|
||||||
{
|
{
|
||||||
RootPath = "files";
|
|
||||||
this.fileRepository = fileRepository;
|
this.fileRepository = fileRepository;
|
||||||
|
this.fileStorageRepository = fileStorageRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FileInfoDto> MoveAsync(int idWell, int? idUser, int idCategory,
|
public async Task<FileInfoDto> MoveAsync(int idWell, int? idUser, int idCategory,
|
||||||
@ -30,10 +30,7 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
{
|
{
|
||||||
destinationFileName = Path.GetFileName(destinationFileName);
|
destinationFileName = Path.GetFileName(destinationFileName);
|
||||||
srcFilePath = Path.GetFullPath(srcFilePath);
|
srcFilePath = Path.GetFullPath(srcFilePath);
|
||||||
if (!File.Exists(srcFilePath))
|
var fileSize = fileStorageRepository.GetLengthFile(srcFilePath);
|
||||||
throw new ArgumentInvalidException($"file {srcFilePath} doesn't exist", nameof(srcFilePath));
|
|
||||||
|
|
||||||
var sysFileInfo = new System.IO.FileInfo(srcFilePath);
|
|
||||||
|
|
||||||
//save info to db
|
//save info to db
|
||||||
var dto = new FileInfoDto {
|
var dto = new FileInfoDto {
|
||||||
@ -41,14 +38,13 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
IdAuthor = idUser,
|
IdAuthor = idUser,
|
||||||
IdCategory = idCategory,
|
IdCategory = idCategory,
|
||||||
Name = destinationFileName,
|
Name = destinationFileName,
|
||||||
Size = sysFileInfo.Length
|
Size = fileSize
|
||||||
};
|
};
|
||||||
var fileId = await fileRepository.InsertAsync(dto, token)
|
var fileId = await fileRepository.InsertAsync(dto, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
string filePath = MakeFilePath(idWell, idCategory, destinationFileName, fileId);
|
string filePath = MakeFilePath(idWell, idCategory, destinationFileName, fileId);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
fileStorageRepository.MoveFile(srcFilePath, filePath);
|
||||||
File.Move(srcFilePath, filePath);
|
|
||||||
|
|
||||||
return await GetInfoAsync(fileId, token);
|
return await GetInfoAsync(fileId, token);
|
||||||
}
|
}
|
||||||
@ -71,18 +67,14 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
|
|
||||||
//save stream to disk
|
//save stream to disk
|
||||||
string filePath = MakeFilePath(idWell, idCategory, fileFullName, fileId);
|
string filePath = MakeFilePath(idWell, idCategory, fileFullName, fileId);
|
||||||
|
await fileStorageRepository.CopyFileAsync(filePath, fileStream, token);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
|
||||||
|
|
||||||
using var newfileStream = new FileStream(filePath, FileMode.Create);
|
|
||||||
await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return await GetInfoAsync(fileId, token);
|
return await GetInfoAsync(fileId, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId)
|
private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId)
|
||||||
{
|
{
|
||||||
return Path.Combine(RootPath, $"{idWell}",
|
return Path.Combine(fileStorageRepository.RootPath, $"{idWell}",
|
||||||
$"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}");
|
$"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,10 +87,7 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
|
|
||||||
var relativePath = GetUrl(dto.IdWell, dto.IdCategory, dto.Id, ext);
|
var relativePath = GetUrl(dto.IdWell, dto.IdCategory, dto.Id, ext);
|
||||||
var fullPath = Path.GetFullPath(relativePath);
|
var fullPath = Path.GetFullPath(relativePath);
|
||||||
if (!File.Exists(fullPath))
|
fileStorageRepository.FileExists(fullPath, relativePath);
|
||||||
{
|
|
||||||
throw new FileNotFoundException("not found", relativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
@ -119,8 +108,7 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
var fileName = GetUrl(file.IdWell, file.IdCategory, file.Id, Path.GetExtension(file.Name));
|
var fileName = GetUrl(file.IdWell, file.IdCategory, file.Id, Path.GetExtension(file.Name));
|
||||||
if (File.Exists(fileName))
|
fileStorageRepository.DeleteFile(fileName);
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.Any() ? 1 : 0;
|
return files.Any() ? 1 : 0;
|
||||||
@ -137,7 +125,7 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name));
|
GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name));
|
||||||
|
|
||||||
public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) =>
|
public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) =>
|
||||||
Path.Combine(RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
|
Path.Combine(fileStorageRepository.RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
|
||||||
|
|
||||||
public Task<int> MarkFileMarkAsDeletedAsync(int idMark,
|
public Task<int> MarkFileMarkAsDeletedAsync(int idMark,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
@ -153,12 +141,9 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
var ext = Path.GetExtension(entity.Name);
|
var ext = Path.GetExtension(entity.Name);
|
||||||
var relativePath = GetUrl(entity.IdWell, entity.IdCategory, entity.Id, ext);
|
var relativePath = GetUrl(entity.IdWell, entity.IdCategory, entity.Id, ext);
|
||||||
var fullPath = Path.GetFullPath(relativePath);
|
var fullPath = Path.GetFullPath(relativePath);
|
||||||
if (!File.Exists(fullPath))
|
fileStorageRepository.FileExists(fullPath, relativePath);
|
||||||
{
|
|
||||||
throw new FileNotFoundException("not found", relativePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,8 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
generator.Make(reportFileName);
|
generator.Make(reportFileName);
|
||||||
|
|
||||||
var fileRepository = new FileRepository(context);
|
var fileRepository = new FileRepository(context);
|
||||||
var fileService = new FileService(fileRepository);
|
var fileStorageRepository = new FileStorageRepository();
|
||||||
|
var fileService = new FileService(fileRepository, fileStorageRepository);
|
||||||
var fileInfo = await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token);
|
var fileInfo = await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token);
|
||||||
|
|
||||||
progressHandler.Invoke(new
|
progressHandler.Invoke(new
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using Moq;
|
using Moq;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -11,7 +9,7 @@ namespace AsbCloudWebApi.Tests
|
|||||||
{
|
{
|
||||||
public class RepositoryFactory
|
public class RepositoryFactory
|
||||||
{
|
{
|
||||||
public static Mock<TRepository> Make<TRepository, TDto>(IList<TDto> data)
|
public static Mock<TRepository> Make<TRepository, TDto>(ICollection<TDto> data)
|
||||||
where TDto : AsbCloudApp.Data.IId
|
where TDto : AsbCloudApp.Data.IId
|
||||||
where TRepository : class, ICrudService<TDto>
|
where TRepository : class, ICrudService<TDto>
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudDb.Model;
|
using AsbCloudDb.Model;
|
||||||
|
using AsbCloudInfrastructure.Repository;
|
||||||
using AsbCloudInfrastructure.Services;
|
using AsbCloudInfrastructure.Services;
|
||||||
using DocumentFormat.OpenXml.Spreadsheet;
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
using DocumentFormat.OpenXml.Wordprocessing;
|
using DocumentFormat.OpenXml.Wordprocessing;
|
||||||
@ -136,7 +137,11 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
|||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
fileService = new FileService(repositoryMock.Object);
|
var storageRepositoryMock = new Mock<IFileStorageRepository>();
|
||||||
|
|
||||||
|
storageRepositoryMock.Setup(x => x.RootPath).Returns("files");
|
||||||
|
|
||||||
|
fileService = new FileService(repositoryMock.Object, storageRepositoryMock.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -147,27 +152,23 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetInfoAsync_throws_FileNotFoundException()
|
public async Task GetInfoAsync_returns_FileInfo()
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<FileNotFoundException>(async () =>
|
var data = await fileService.GetInfoAsync(1742, CancellationToken.None);
|
||||||
{
|
Assert.NotNull(data);
|
||||||
await fileService.GetInfoAsync(1743, CancellationToken.None);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetInfoByIdsAsync_throws_FileNotFoundException()
|
public async Task GetInfoByIdsAsync_returns_FileInfo()
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<FileNotFoundException>(async () =>
|
var data = await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None);
|
||||||
{
|
Assert.NotNull(data);
|
||||||
await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SaveAsync_returns_FileInfoa()
|
public async Task SaveAsync_returns_FileInfoa()
|
||||||
{
|
{
|
||||||
var stream = new FileStream("D:\\test\\test.txt", FileMode.Open);
|
using var stream = new MemoryStream();
|
||||||
var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None);
|
var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None);
|
||||||
Assert.NotNull(data);
|
Assert.NotNull(data);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user