Я пытаюсь сделать таблицу, в которой перечислены 2 типа файлов, связанных вместе. Один тип — это файлы .mp3, а другой — файлы .txt. Я хочу, чтобы эти файлы были связаны друг с другом, чтобы файлы с одинаковым именем делили одну строку, когда через них проходит цикл foreach. Это делается для того, чтобы можно было воспроизводить файлы mp3 и открывать соответствующий текстовый файл.
На странице App.razor есть таблица, в которой отображаются все файлы в папке, но не учитывается, имеют ли файлы двух типов одно и то же имя. Может ли кто-нибудь помочь с тем, как создать класс, в котором файлы связаны вместе, чтобы их можно было вызывать в таблице?
Вот код.
<table class = "table table-striped mb-0">
<thead>
<tr>
<th scope = "col">Name</th>
<th scope = "col">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var file in textList)
{
<tr>
<td>
@file.Name
</td>
<td>
<span @onclick = "() => PlayAudio(file.Url)"
class = "text-primary oi oi-play-circle me-2" aria-hidden = "true" role = "button">
</span>
<span @onclick = "() => DeleteAudio(file)"
class = "text-danger oi oi-trash" aria-hidden = "true" role = "button">
</span>
<span @onclick = "() => openTextFile(file)"
><button>Open</button>
</span>
</td>
</tr>
}
}
</tbody>
</table>
@code{
readonly List<TextFile> textList = new();
readonly string FolderName = "textSoundFiles";
protected override void OnInitialized()
{
var path = $"{env.WebRootPath}\\{FolderName}\\";
var files = new DirectoryInfo(path).GetFiles();
foreach (var file in files)
{
textList.Add(new TextFile
{
Name = file.Name,
Url = $"/textFiles/{file.Name}",
Path = file.FullName
});
}
}
public class TextFile
{
public string Name { get; set; }
public string Url { get; set; }
public string Path { get; set; }
}
}
Я пытаюсь создать таблицу со списком файлов, которая позволяет мне воспроизводить аудио из перечисленных аудиофайлов. Я пытаюсь 2 типа файлов в каталоге, так что файлы с одинаковыми именами (за исключением их типа MIME) связаны друг с другом
@BrianParker, как это свяжет файлы вместе?
Они будут сгруппированы по имени, и вы будете перебирать группы. Сами группы будут иметь подсписок, в вашем случае, из одного или двух пунктов. Если ваше имя включает расширение .txt или .mp3, возможно, вам придется написать производное свойство, чтобы удалить его и сгруппировать по нему.
@BrianParker Я наткнулся на System.IO.Path.ChangeExtension(path, null); Который позволил бы мне отрезать расширения, сохранив путь, но это не сработало. Вы можете проверить редактирование в моем посте и посмотреть, что я сделал неправильно?
Используйте Linq GroupBy
@foreach(var fileGroup in myFilesGroupedAndSorted)
{
<h3>@fileGroup.Key</h3>
@foreach(var file in fileGroup.OrderBy( file => file.Path))
{
<div>@file.Path</div>
}
}
@code {
// without access to your folder I generated random data.
List<TextFile> myList = new List<TextFile>
{
new TextFile { Name = "", Path = "some-path\\SomeFile1.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile2.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile3.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile4.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile5.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile6.txt", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile1.mp3", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile2.mp3", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile3.mp3", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile4.mp3", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile5.mp3", Url = "" },
new TextFile { Name = "", Path = "some-path\\SomeFile6.mp3", Url = "" },
};
IEnumerable<IGrouping<string, TextFile>> myFilesGroupedAndSorted
=> myList.GroupBy(file => GetPathWithoutExtension(file.Path))
.OrderBy(group => group.Key);
private string GetPathWithoutExtension(string path)
{
return System.IO.Path.ChangeExtension(path, null);
}
}
Затем обработка каждой группы должна быть тривиальной путем тестирования каждого расширения файла. Я бы сделал подкомпонент для передачи IGrouping<string, TextFile> в качестве параметра, чтобы упростить логику...
Mp3.Razor
<tr>
<td>
@FileGroup.Key
</td>
<td>
@if (Mp3 is not null)
{
<span @onclick = "() => PlayAudio(Mp3.Url)"
class = "text-primary oi oi-play-circle me-2" aria-hidden = "true" role = "button">
</span>
<span @onclick = "() => DeleteAudio(FileGroup.Key)"
class = "text-danger oi oi-trash" aria-hidden = "true" role = "button">
</span>
}
@if (Text is not null)
{
<span @onclick = "() => openTextFile(Text)">
<button>Open</button>
</span>
}
</td>
</tr>
@code {
[Parameter]
public IGrouping<string, TextFile> FileGroup { get; set; } = default!;
TextFile? Text => FileGroup.FirstOrDefault(file => Path.GetExtension(file.Path).ToLower() == "txt");
TextFile? Mp3 => FileGroup.FirstOrDefault(file => Path.GetExtension(file.Path).ToLower() == "mp3");
}
...
<tbody>
@foreach(var fileGroup in myFilesGroupedAndSorted)
{
<Mp3 FileGroup=fileGroup />
}
</tbody>
...
Я пробовал кое-что, но список так и не появился. Могу я попросить вас проверить редактирование кода в исходном сообщении и посмотреть, что не так?
@Addlon Во-первых, заполняется ли список должным образом? Можете ли вы поставить точку останова и проверить ее содержимое?
Я получаю исключение System.ArgumentNullException: 'Value cannot be null. (Parameter 'source')' в Text => FileGroup... и Mp3 =>....
Извините, что беспокою вас, но в таблице теперь перечислены @fileGroup.Key, но только когда TextFile? Text => fileGroup... и TextFile? Mp3 => fileGroup... удалены, а также 2 оператора if, в которых они упоминаются. Когда они там, они получают исключение ArgumentNullException. Вы знаете, почему это происходит?
@Addlon Ошибка — это фильтр для расширения. Возврат Path.GetExtension включает ., поэтому замените два == "mp3" на = = ".mp3". Это была опечатка от моего имени.
Я могу с сожалением сказать, что ошибка все еще сохраняется с '.' добавлен
@Addlon github.com/BrianLParker/TwoFilesLinkedQuestionOnStack
У меня есть аудиоэлемент в Mp3Component, который находится за пределами <tr>...</tr>, но вызывается в цикле foreach. Есть ли способ отделить аудиоэлемент в Mp3Component от того, что происходит в цикле, чтобы на него можно было ссылаться вне foreach?
Вам нужно «повернуть» таблицу, т.е. преобразовать список в таблицу. Для этого можно использовать LINQ. Проверьте этот ответ. Вместо «Jan» и «Feb» у вас будут свойства bool HasMp3 и bool HasTxt с соответствующими пунктами Where. Что-то вроде этого:
var files = new DirectoryInfo(path).GetFiles();
textList.AddRange(files
.GroupBy(c => Path.ChangeExtension(c.Name, null))
.Select(g => new TextFile() {
Name = g.Key,
Url = $"/textFiles/{g.Key}",
HasMp3 = g.Any(c => Path.GetExtension(c) == ".mp3"),
HasTxt = g.Any(c => Path.GetExtension(c) == ".txt"),
}));
С помощью этих двух логических свойств вы можете скрыть кнопки, если соответствующий файл не существует. Url свойство не имеет расширения. Вам нужно будет добавить каждое подходящее расширение в каждую подходящую функцию.
Повторить files.GroupBy(file => file.Name)