mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +01:00
FileService: AddFile from URL (#3566)
This commit is contained in:
parent
e449ca2c95
commit
ef6016857b
5 changed files with 79 additions and 19 deletions
|
@ -6,7 +6,6 @@ namespace BTCPayServer.Configuration
|
||||||
public string PluginDir { get; set; }
|
public string PluginDir { get; set; }
|
||||||
public string TempStorageDir { get; set; }
|
public string TempStorageDir { get; set; }
|
||||||
public string StorageDir { get; set; }
|
public string StorageDir { get; set; }
|
||||||
|
public string TempDir { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ public interface IFileService
|
||||||
{
|
{
|
||||||
Task<bool> IsAvailable();
|
Task<bool> IsAvailable();
|
||||||
Task<IStoredFile> AddFile(IFormFile file, string userId);
|
Task<IStoredFile> AddFile(IFormFile file, string userId);
|
||||||
|
Task<IStoredFile> AddFile(Uri file, string userId);
|
||||||
Task<string?> GetFileUrl(Uri baseUri, string fileId);
|
Task<string?> GetFileUrl(Uri baseUri, string fileId);
|
||||||
Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
|
Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
|
||||||
bool isDownload);
|
bool isDownload);
|
||||||
|
|
|
@ -394,6 +394,7 @@ namespace BTCPayServer
|
||||||
dataDirectories.PluginDir = configuration["plugindir"] ?? defaultSettings.DefaultPluginDirectory;
|
dataDirectories.PluginDir = configuration["plugindir"] ?? defaultSettings.DefaultPluginDirectory;
|
||||||
dataDirectories.StorageDir = Path.Combine(dataDirectories.DataDir, Storage.Services.Providers.FileSystemStorage.FileSystemFileProviderService.LocalStorageDirectoryName);
|
dataDirectories.StorageDir = Path.Combine(dataDirectories.DataDir, Storage.Services.Providers.FileSystemStorage.FileSystemFileProviderService.LocalStorageDirectoryName);
|
||||||
dataDirectories.TempStorageDir = Path.Combine(dataDirectories.StorageDir, "tmp");
|
dataDirectories.TempStorageDir = Path.Combine(dataDirectories.StorageDir, "tmp");
|
||||||
|
dataDirectories.TempDir = Path.Combine(dataDirectories.DataDir, "tmp");
|
||||||
return dataDirectories;
|
return dataDirectories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,85 +1,128 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using BTCPayServer.Abstractions.Extensions;
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
|
using BTCPayServer.Configuration;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Storage.Models;
|
using BTCPayServer.Storage.Models;
|
||||||
using BTCPayServer.Storage.Services.Providers;
|
using BTCPayServer.Storage.Services.Providers;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace BTCPayServer.Storage.Services
|
namespace BTCPayServer.Storage.Services
|
||||||
{
|
{
|
||||||
public class FileService : IFileService
|
public class FileService : IFileService
|
||||||
{
|
{
|
||||||
private readonly StoredFileRepository _FileRepository;
|
private readonly StoredFileRepository _fileRepository;
|
||||||
private readonly IEnumerable<IStorageProviderService> _providers;
|
private readonly IEnumerable<IStorageProviderService> _providers;
|
||||||
private readonly SettingsRepository _SettingsRepository;
|
private readonly SettingsRepository _settingsRepository;
|
||||||
|
private readonly IOptions<DataDirectories> _dataDirectories;
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
|
||||||
public FileService(StoredFileRepository fileRepository, IEnumerable<IStorageProviderService> providers,
|
public FileService(StoredFileRepository fileRepository,
|
||||||
SettingsRepository settingsRepository)
|
SettingsRepository settingsRepository,
|
||||||
|
IEnumerable<IStorageProviderService> providers,
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IOptions<DataDirectories> dataDirectories)
|
||||||
{
|
{
|
||||||
_FileRepository = fileRepository;
|
_fileRepository = fileRepository;
|
||||||
_providers = providers;
|
_providers = providers;
|
||||||
_SettingsRepository = settingsRepository;
|
_settingsRepository = settingsRepository;
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_dataDirectories = dataDirectories;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsAvailable()
|
public async Task<bool> IsAvailable()
|
||||||
{
|
{
|
||||||
var settings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
var settings = await _settingsRepository.GetSettingAsync<StorageSettings>();
|
||||||
return settings is not null;
|
return settings is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IStoredFile> AddFile(IFormFile file, string userId)
|
public async Task<IStoredFile> AddFile(IFormFile file, string userId)
|
||||||
{
|
{
|
||||||
var settings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
var settings = await _settingsRepository.GetSettingAsync<StorageSettings>();
|
||||||
if (settings is null)
|
if (settings is null)
|
||||||
throw new InvalidOperationException("StoreSettings not configured");
|
throw new InvalidOperationException("StoreSettings not configured");
|
||||||
if (!file.FileName.IsValidFileName())
|
if (!file.FileName.IsValidFileName())
|
||||||
throw new InvalidOperationException("Invalid file name");
|
throw new InvalidOperationException("Invalid file name");
|
||||||
|
|
||||||
var provider = GetProvider(settings);
|
var provider = GetProvider(settings);
|
||||||
|
|
||||||
var storedFile = await provider.AddFile(file, settings);
|
var storedFile = await provider.AddFile(file, settings);
|
||||||
storedFile.ApplicationUserId = userId;
|
storedFile.ApplicationUserId = userId;
|
||||||
await _FileRepository.AddFile(storedFile);
|
await _fileRepository.AddFile(storedFile);
|
||||||
|
return storedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IStoredFile> AddFile(Uri url, string userId)
|
||||||
|
{
|
||||||
|
if (!await IsAvailable())
|
||||||
|
throw new InvalidOperationException("StoreSettings not configured");
|
||||||
|
|
||||||
|
var fileName = Path.GetFileName(url.AbsolutePath);
|
||||||
|
if (!fileName.IsValidFileName())
|
||||||
|
throw new InvalidOperationException("Invalid file name");
|
||||||
|
|
||||||
|
// download
|
||||||
|
var filePath = Path.Join(_dataDirectories.Value.TempDir, fileName);
|
||||||
|
var httClient = _httpClientFactory.CreateClient();
|
||||||
|
using var resp = await httClient.GetAsync(url);
|
||||||
|
await using var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
|
||||||
|
await resp.Content.CopyToAsync(stream);
|
||||||
|
var file = new FormFile(stream, 0, stream.Length, fileName, fileName)
|
||||||
|
{
|
||||||
|
Headers = new HeaderDictionary(),
|
||||||
|
ContentType = GetContentType(filePath)
|
||||||
|
};
|
||||||
|
await stream.FlushAsync();
|
||||||
|
|
||||||
|
var storedFile = await AddFile(file, userId);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
File.Delete(filePath);
|
||||||
|
|
||||||
return storedFile;
|
return storedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetFileUrl(Uri baseUri, string fileId)
|
public async Task<string?> GetFileUrl(Uri baseUri, string fileId)
|
||||||
{
|
{
|
||||||
var settings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
var settings = await _settingsRepository.GetSettingAsync<StorageSettings>();
|
||||||
if (settings is null)
|
if (settings is null)
|
||||||
return null;
|
return null;
|
||||||
var provider = GetProvider(settings);
|
var provider = GetProvider(settings);
|
||||||
var storedFile = await _FileRepository.GetFile(fileId);
|
var storedFile = await _fileRepository.GetFile(fileId);
|
||||||
return storedFile == null ? null : await provider.GetFileUrl(baseUri, storedFile, settings);
|
return storedFile == null ? null : await provider.GetFileUrl(baseUri, storedFile, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
|
public async Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
|
||||||
bool isDownload)
|
bool isDownload)
|
||||||
{
|
{
|
||||||
var settings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
var settings = await _settingsRepository.GetSettingAsync<StorageSettings>();
|
||||||
if (settings is null)
|
if (settings is null)
|
||||||
return null;
|
return null;
|
||||||
var provider = GetProvider(settings);
|
var provider = GetProvider(settings);
|
||||||
var storedFile = await _FileRepository.GetFile(fileId);
|
var storedFile = await _fileRepository.GetFile(fileId);
|
||||||
return storedFile == null ? null : await provider.GetTemporaryFileUrl(baseUri, storedFile, settings, expiry, isDownload);
|
return storedFile == null ? null : await provider.GetTemporaryFileUrl(baseUri, storedFile, settings, expiry, isDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveFile(string fileId, string userId)
|
public async Task RemoveFile(string fileId, string userId)
|
||||||
{
|
{
|
||||||
var settings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
var settings = await _settingsRepository.GetSettingAsync<StorageSettings>();
|
||||||
if (settings is null)
|
if (settings is null)
|
||||||
return;
|
return;
|
||||||
var provider = GetProvider(settings);
|
var provider = GetProvider(settings);
|
||||||
var storedFile = await _FileRepository.GetFile(fileId);
|
var storedFile = await _fileRepository.GetFile(fileId);
|
||||||
if (string.IsNullOrEmpty(userId) ||
|
if (string.IsNullOrEmpty(userId) ||
|
||||||
storedFile.ApplicationUserId.Equals(userId, StringComparison.InvariantCultureIgnoreCase))
|
storedFile.ApplicationUserId.Equals(userId, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
await provider.RemoveFile(storedFile, settings);
|
await provider.RemoveFile(storedFile, settings);
|
||||||
await _FileRepository.RemoveFile(storedFile);
|
await _fileRepository.RemoveFile(storedFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,5 +130,16 @@ namespace BTCPayServer.Storage.Services
|
||||||
{
|
{
|
||||||
return _providers.First((service) => service.StorageProvider().Equals(storageSettings.Provider));
|
return _providers.First((service) => service.StorageProvider().Equals(storageSettings.Provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetContentType(string filePath)
|
||||||
|
{
|
||||||
|
var mimeProvider = new FileExtensionContentTypeProvider();
|
||||||
|
if (!mimeProvider.TryGetContentType(filePath, out string? contentType))
|
||||||
|
{
|
||||||
|
contentType = "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,11 @@ namespace BTCPayServer.Storage
|
||||||
{
|
{
|
||||||
dirInfo = new DirectoryInfo(datadirs.Value.StorageDir);
|
dirInfo = new DirectoryInfo(datadirs.Value.StorageDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Directory.Exists(datadirs.Value.TempDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(datadirs.Value.TempDir);
|
||||||
|
}
|
||||||
|
|
||||||
DirectoryInfo tmpdirInfo;
|
DirectoryInfo tmpdirInfo;
|
||||||
if (!Directory.Exists(datadirs.Value.TempStorageDir))
|
if (!Directory.Exists(datadirs.Value.TempStorageDir))
|
||||||
|
|
Loading…
Add table
Reference in a new issue