Skip to content

Commit 4e08d81

Browse files
authored
Merge pull request #104 from dotnet-campus/t/lindexi/UsingHardLinkToZipNtfsDiskSize
添加使用硬连接减少磁盘空间占用工具
2 parents de9be19 + fcb3199 commit 4e08d81

19 files changed

Lines changed: 884 additions & 0 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Application x:Class="UsingHardLinkToZipNtfsDiskSize.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:UsingHardLinkToZipNtfsDiskSize"
5+
StartupUri="MainWindow.xaml">
6+
<Application.Resources>
7+
8+
</Application.Resources>
9+
</Application>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Configuration;
2+
using System.Data;
3+
using System.Windows;
4+
5+
namespace UsingHardLinkToZipNtfsDiskSize;
6+
/// <summary>
7+
/// Interaction logic for App.xaml
8+
/// </summary>
9+
public partial class App : Application
10+
{
11+
}
12+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Windows;
2+
3+
[assembly: ThemeInfo(
4+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5+
//(used if a resource is not found in the page,
6+
// or application resource dictionaries)
7+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8+
//(used if a resource is not found in the page,
9+
// app, or any theme specific resource dictionaries)
10+
)]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System.Threading.Channels;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace UsingHardLinkToZipNtfsDiskSize;
5+
6+
public class ChannelLoggerProvider : ILoggerProvider
7+
{
8+
public ChannelLoggerProvider(params IStringLoggerWriter[] stringLoggerWriterList)
9+
{
10+
_stringLoggerWriterList = stringLoggerWriterList;
11+
var channel = Channel.CreateUnbounded<string>(new UnboundedChannelOptions()
12+
{
13+
SingleReader = true
14+
});
15+
_channel = channel;
16+
17+
Task.Run(WriteLogAsync);
18+
}
19+
20+
private readonly IStringLoggerWriter[] _stringLoggerWriterList;
21+
22+
private async Task? WriteLogAsync()
23+
{
24+
while (!_channel.Reader.Completion.IsCompleted)
25+
{
26+
try
27+
{
28+
var message = await _channel.Reader.ReadAsync();
29+
foreach (var stringLoggerWriter in _stringLoggerWriterList)
30+
{
31+
await stringLoggerWriter.WriteAsync(message);
32+
}
33+
}
34+
catch (ChannelClosedException)
35+
{
36+
// 结束
37+
}
38+
}
39+
40+
foreach (var stringLoggerWriter in _stringLoggerWriterList)
41+
{
42+
await stringLoggerWriter.DisposeAsync();
43+
}
44+
}
45+
46+
private readonly Channel<string> _channel;
47+
public void Dispose()
48+
{
49+
ChannelWriter<string> channelWriter = _channel.Writer;
50+
channelWriter.TryComplete();
51+
}
52+
53+
public ILogger CreateLogger(string categoryName)
54+
{
55+
return new ChannelLogger(_channel.Writer);
56+
}
57+
58+
class ChannelLogger : ILogger, IDisposable
59+
{
60+
public ChannelLogger(ChannelWriter<string> writer)
61+
{
62+
_writer = writer;
63+
}
64+
65+
private readonly ChannelWriter<string> _writer;
66+
67+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
68+
{
69+
var message = $"{DateTime.Now:yyyy.MM.dd HH:mm:ss,fff} [{logLevel}][{eventId}] {formatter(state, exception)}";
70+
_ = _writer.WriteAsync(message);
71+
}
72+
73+
public bool IsEnabled(LogLevel logLevel)
74+
{
75+
return true;
76+
}
77+
78+
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
79+
{
80+
return this;
81+
}
82+
83+
public void Dispose()
84+
{
85+
}
86+
}
87+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Windows.Controls;
2+
3+
namespace UsingHardLinkToZipNtfsDiskSize;
4+
5+
class DispatcherStringLoggerWriter : IStringLoggerWriter
6+
{
7+
public DispatcherStringLoggerWriter(TextBlock logTextBlock)
8+
{
9+
_logTextBlock = logTextBlock;
10+
}
11+
12+
private readonly TextBlock _logTextBlock;
13+
14+
private string _lastMessage = string.Empty;
15+
private bool _isInvalidate = false;
16+
17+
public ValueTask WriteAsync(string message)
18+
{
19+
_lastMessage = message;
20+
21+
if (!_isInvalidate)
22+
{
23+
_isInvalidate = true;
24+
25+
_logTextBlock.Dispatcher.InvokeAsync(() =>
26+
{
27+
_logTextBlock.Text = _lastMessage;
28+
_isInvalidate = false;
29+
});
30+
}
31+
32+
return ValueTask.CompletedTask;
33+
}
34+
35+
public ValueTask DisposeAsync()
36+
{
37+
return ValueTask.CompletedTask;
38+
}
39+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace UsingHardLinkToZipNtfsDiskSize;
4+
5+
public class FileRecordModel
6+
{
7+
[Key]
8+
[Required]
9+
public string FilePath { set; get; } = null!;
10+
11+
[Required]
12+
public long FileLength { set; get; }
13+
14+
[Required]
15+
public string FileSha1Hash { set; get; } = null!;
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace UsingHardLinkToZipNtfsDiskSize;
4+
5+
public class FileStorageContext : DbContext
6+
{
7+
public FileStorageContext()
8+
{
9+
// 用于设计时
10+
_sqliteFile = "FileManger.db";
11+
}
12+
13+
public FileStorageContext(string sqliteFile)
14+
{
15+
_sqliteFile = sqliteFile;
16+
}
17+
18+
private readonly string _sqliteFile;
19+
20+
public DbSet<FileStorageModel> FileStorageModel { set; get; } = null!;
21+
public DbSet<FileRecordModel> FileRecordModel { set; get; } = null!;
22+
23+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
24+
{
25+
optionsBuilder
26+
.UseSqlite($"Filename={_sqliteFile}");
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace UsingHardLinkToZipNtfsDiskSize;
4+
5+
public class FileStorageModel
6+
{
7+
[Key]
8+
[Required]
9+
public string FileSha1Hash { set; get; } = null!;
10+
11+
/// <summary>
12+
/// 原始的文件路径
13+
/// </summary>
14+
[Required]
15+
public string OriginFilePath { set; get; } = null!;
16+
17+
/// <summary>
18+
/// 有多少个文件引用了
19+
/// </summary>
20+
public long ReferenceCount { set; get; }
21+
22+
/// <summary>
23+
/// 文件大小
24+
/// </summary>
25+
public long FileLength { set; get; }
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace UsingHardLinkToZipNtfsDiskSize;
2+
3+
public interface IStringLoggerWriter : IAsyncDisposable
4+
{
5+
ValueTask WriteAsync(string message);
6+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.IO;
2+
using System.Text;
3+
4+
namespace UsingHardLinkToZipNtfsDiskSize;
5+
6+
public class LogFileStringLoggerWriter : IStringLoggerWriter
7+
{
8+
public LogFileStringLoggerWriter(DirectoryInfo logFolder)
9+
{
10+
_logFolder = logFolder;
11+
var logFile = Path.Join(logFolder.FullName, "Log.txt");
12+
var fileStream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
13+
_fileStream = fileStream;
14+
var streamWriter = new StreamWriter(fileStream, Encoding.UTF8);
15+
_streamWriter = streamWriter;
16+
}
17+
18+
private readonly DirectoryInfo _logFolder;
19+
private readonly FileStream _fileStream;
20+
private readonly StreamWriter _streamWriter;
21+
22+
public async ValueTask WriteAsync(string message)
23+
{
24+
await _streamWriter.WriteLineAsync(message);
25+
}
26+
27+
public async ValueTask DisposeAsync()
28+
{
29+
await _streamWriter.DisposeAsync();
30+
await _fileStream.DisposeAsync();
31+
}
32+
}

0 commit comments

Comments
 (0)