Skip to content

Commit 18b9ecd

Browse files
committed
#445 add convert filename if not allowed chars are inside and a test for the function
1 parent 9ee5aa3 commit 18b9ecd

File tree

6 files changed

+126
-1
lines changed

6 files changed

+126
-1
lines changed

Components/IO/BExIS.IO/BExIS.IO.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
<Project>{6EAD7D02-02F7-42FF-85E4-90BB892D3846}</Project>
7373
<Name>BExIS.Utils.Config</Name>
7474
</ProjectReference>
75+
<ProjectReference Include="..\..\Utils\BExIS.Utils\BExIS.Utils.csproj">
76+
<Project>{782b71c1-707f-4ab1-80e9-90d2880635b4}</Project>
77+
<Name>BExIS.Utils</Name>
78+
</ProjectReference>
7579
</ItemGroup>
7680
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
7781
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />

Components/IO/BExIS.Io/IoHelper.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BExIS.Utils.Config;
2+
using BExIS.Utils.Files;
23
using System;
34
using System.IO;
45

@@ -42,7 +43,9 @@ public static string GetFileName(FileType type, long datasetId, int versionNr, l
4243
string downloadName = "no title available";
4344
string downloadDate = DateTime.Now.ToString("yyyyMMdd");
4445

45-
string downloadTitle = title.Replace(" ", "");
46+
string downloadTitle = FileNameUtility.SanitizeFileName(title,'-');
47+
48+
4649

4750
// tag vs version?
4851

Components/Utils/BExIS.Utils.Tests/BExIS.Utils.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
</ProjectReference>
127127
</ItemGroup>
128128
<ItemGroup>
129+
<Compile Include="Files\FileNameUtilityTests.cs" />
129130
<Compile Include="Helper\HashHelperTests.cs" />
130131
<Compile Include="Helper\ManualHelperTests.cs" />
131132
<Compile Include="Properties\AssemblyInfo.cs" />
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using BExIS.Utils.Files;
2+
using BExIS.Utils.Helpers;
3+
using NUnit.Framework;
4+
5+
namespace BExIS.Utils.Tests
6+
{
7+
[TestFixture()]
8+
public class FileNameUtilityTests
9+
{
10+
[OneTimeSetUp]
11+
public void OneTimeSetUp()
12+
{
13+
}
14+
15+
[OneTimeTearDown]
16+
public void OneTimeTearDown()
17+
{
18+
}
19+
20+
[Test]
21+
public void SanitizeFileName_invalidCharacters_ReturnValid()
22+
{
23+
//Arrange
24+
string input = "file<name>with:forbidden\"chars/\\|?*.txt";
25+
string expected = "file-name-with-forbidden-chars-.txt";
26+
27+
//Act
28+
string result = FileNameUtility.SanitizeFileName(input);
29+
30+
//Assert
31+
Assert.NotNull(result);
32+
Assert.AreEqual(expected, result);
33+
}
34+
35+
36+
}
37+
}

Components/Utils/BExIS.Utils/BExIS.Utils.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
<Compile Include="Extensions\QueryableExtensions.cs" />
102102
<Compile Include="Extensions\ViewExtentions.cs" />
103103
<Compile Include="Extensions\ZipArchiveExtensions.cs" />
104+
<Compile Include="Files\FileNameUtility.cs" />
104105
<Compile Include="Filters\MustBeTrueAttribute.cs" />
105106
<Compile Include="Filters\QueryAttribute.cs" />
106107
<Compile Include="Helpers\HashHelper.cs" />
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Text;
4+
5+
6+
namespace BExIS.Utils.Files
7+
{
8+
/// <summary>
9+
/// Provides utility methods for handling and sanitizing file names based on operating system rules.
10+
/// </summary>
11+
public static class FileNameUtility
12+
{
13+
/// <summary>
14+
/// Replaces all invalid file name characters with a specified replacement character.
15+
/// It ensures the resulting string is safe to use as a file name segment on Windows/Unix-like systems.
16+
/// </summary>
17+
/// <param name="fileName">The original file name string to sanitize.</param>
18+
/// <param name="replacementChar">The character to use for replacing invalid characters (default is '-').</param>
19+
/// <returns>A sanitized string suitable for use as a file name.</returns>
20+
public static string SanitizeFileName(string fileName, char replacementChar = '-')
21+
{
22+
// 1. Check for null or empty input
23+
if (string.IsNullOrEmpty(fileName))
24+
{
25+
return string.Empty;
26+
}
27+
28+
// 2. Get the array of characters forbidden in file names by the operating system.
29+
// This is the most reliable way to handle OS-specific invalid characters.
30+
char[] invalidChars = Path.GetInvalidFileNameChars();
31+
32+
// 3. Use a StringBuilder for efficient string manipulation
33+
StringBuilder sb = new StringBuilder(fileName.Length);
34+
35+
// 4. Iterate through the input string and replace invalid characters
36+
foreach (char c in fileName)
37+
{
38+
if (invalidChars.Contains(c))
39+
{
40+
// Replace the invalid character with the specified replacement char
41+
sb.Append(replacementChar);
42+
}
43+
else
44+
{
45+
// Keep valid characters as they are
46+
sb.Append(c);
47+
}
48+
}
49+
50+
string sanitizedName = sb.ToString();
51+
52+
// 5. Post-sanitization cleanup for specific Windows constraints (ends with '.' or ' ')
53+
// While replacement should handle most issues, it's good practice to ensure
54+
// the name doesn't end in characters that Windows implicitly trims or handles poorly.
55+
56+
// Trim spaces from the end
57+
sanitizedName = sanitizedName.TrimEnd(' ');
58+
59+
// If the name ends with the replacement char (e.g., if the original ended with a '.'),
60+
// ensure it doesn't leave an ending period (Windows specific) unless it's part of an extension.
61+
// We'll use a simple check to ensure it doesn't end with a period.
62+
if (sanitizedName.EndsWith("."))
63+
{
64+
// Remove trailing period if it exists
65+
sanitizedName = sanitizedName.TrimEnd('.');
66+
}
67+
68+
// Optional: Replace multiple consecutive replacement characters with a single one.
69+
// This prevents "file---name--.txt" from becoming "file-name.txt".
70+
string doubleReplacement = new string(new[] { replacementChar, replacementChar });
71+
while (sanitizedName.Contains(doubleReplacement))
72+
{
73+
sanitizedName = sanitizedName.Replace(doubleReplacement, replacementChar.ToString());
74+
}
75+
76+
return sanitizedName;
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)