1. 作业内容
编写一个C#程序,在作业二实现的本地播放功能的基础上,新增在线播放和在线下载功能,作业二博客地址:C#实现简单音乐文件解析播放——Windows程序设计作业2
2. 架构选择
考虑到需求中的界面友好和跨版本兼容性,我选择选择WinForms
作为开发平台,WinForms
提供了一个简单而强大的方法来创建桌面应用程序,并且与C#
高度兼容,在开发过程,选择.NETFramework 4.8.0
版本进行程序设计。
3. UI设计
在本次程序设计中,我设计了一套UI界面,并且利用panel
和button
控件实现简单的界面切换,一共分为三个界面:本地播放界面、在线下载界面和在线播放界面,具体界面如下:
此处使用的图标来源:FLATICON
以下为本次作业涉及到的控件:
1) Button
:用于输入歌名实现搜索功能。
2) TextBox
:用于触发各种操作,例如播放、搜索、下载等。
3) Panel
:用于切换界面。
4) ListBox
:下载用于显示搜索结果列表或歌单列表,用户可以从中选择歌曲。
5) ProgressBar
:用于显示进度。
6) AxWindowsMediaPlayer
:用于播放音乐。
4. 主界面设计
主界面中主要是围绕各个界面的切换设置的,其中将本地播放音乐界面作为主界面UCHome
,剩余两个功能界面定义为UserControl
对象。
// 定义各界面对象
public UCHome uchome;
public UserControl1 userControl1;
public UserControl2 userControl2;
切换界面的思路是,利用panel
控件作为主要的切换面板,在切换界面的时候,清空其中的界面并添加新的界面,从而实现界面切换功能。
uchome = new UCHome(); //实例化
uchome.Show(); // 将窗体一进行显示
panelContain.Controls.Clear(); // 清空原容器上的控件
panelContain.Controls.Add(uchome); // 将窗体一加入容器panelContain
5. 音乐API说明
在本次作业中,设计在线下载和播放使用的音乐api接口是调用了别人进行二次加工后的api接口,简化了开发流程,特别感谢,但不方便透露,故不在此展出,项目链接在最后放出,如有需要,在下载后调整api接口和返回的参数信息处理,仍可实现功能。
6. 功能说明与代码实现
6.1 本地播放音乐
此项功能主要是辨析音乐文件的格式并且针对不同格式的文件采取不同的解析方式,在作业二中已经实现,此处不再过多阐述。
6.2 在线下载音乐
在线下载音乐功能可以分为三个模块实现:搜索歌曲、选择想要下载的歌曲、下载音乐,最终效果如下:
6.2.1 搜索歌曲
搜索歌曲模块通过用户输入的歌曲名称,向远程API发送请求并获取搜索结果。
1) 初始化参数
private int selectedIndex = -1; // 当前选中的索引
private List<string> responseLines = new List<string>(); // 保存响应结果的列表
2) 用户在文本框TextBox
中输入歌曲名称。
string songName = textBox1.Text;
if (string.IsNullOrWhiteSpace(songName))
{
MessageBox.Show("请输入歌曲名称");
return;
}
3) 点击搜索按钮后,调用SearchSongsAsync
方法发送HTTP请求,获取包含搜索结果的响应,将结果处理好显示在ListBox
中供用户选择。
private async void button1_Click(object sender, EventArgs e)
{
/*
此处省略了前面的输入部分
*/
listBox1.Items.Clear();
responseLines.Clear();
var responseBody = await SearchSongsAsync(songName); // 调用搜索歌曲的异步方法
if (!string.IsNullOrEmpty(responseBody))
{
var lines = responseBody.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
responseLines.Add(line);
listBox1.Items.Add(line); // 将结果添加到列表框中
}
}
else
{
MessageBox.Show("未找到歌曲");
}
}
private async Task<string> SearchSongsAsync(string songName)
{
string apiUrl = $"https://www.zbx123456?msg={Uri.EscapeDataString(songName)}";
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(apiUrl);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
}
此处采取async
修饰函数和Task<string>
作为返回值是因为在WindoForms
应用程序中,如果进行长时间的同步操作(如网络请求、文件读写),会导致界面卡顿或无响应。通过使用async
和Task
,可以将这些耗时操作异步执行,从而保持用户界面的响应能力。
其中,async
用于声明异步方法,允许函数中使用await
函数,await
允许程序在执行异步操作时,暂停方法执行,释放UI线程以处理其他任务。当异步操作完成后,恢复方法执行。
Task<string>
表示一个异步操作,该操作最终会返回一个string
结果,使异步方法在逻辑上与同步方法一致,方便处理返回值。
6.2.2 选择想要下载的歌曲
用户在搜索结果列表中选择想要下载的歌曲,通过SelectedIndexChanged
事件处理程序更新选中的索引。
// 列表框选中项改变事件处理程序
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
selectedIndex = listBox1.SelectedIndex + 1;
}
6.2.3 下载音乐
下载音乐模块根据选中的歌曲索引获取下载链接,并下载音乐文件到用户指定的路径。
1) 用户点击下载按钮,调用button2_Click
事件处理程序,先检查是否有选中的歌曲,并异步获取下载链接。
private async void button2_Click(object sender, EventArgs e)
{
if (selectedIndex == -1 || selectedIndex >= responseLines.Count)
{
MessageBox.Show("请选择要下载的歌曲");
return;
}
string downloadUrl = await GetDownloadUrlAsync(selectedIndex); // 调用获取下载链接的异步方法
/*
下载音乐逻辑,后面详细介绍
*/
}
2) 调用GetDownloadUrlAsync
方法获取选中歌曲的下载链接:
private async Task<string> GetDownloadUrlAsync(int n)
{
string apiUrl = $"https://www.zbx123456?msg={Uri.EscapeDataString(textBox1.Text)}&n={n}";
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(apiUrl);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
textBox2.Text = responseBody; // 将响应内容放入textBox2中
// 检查响应内容,解析播放链接
var lines = responseBody.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.StartsWith("播放链接:"))
{
return line.Substring(5).Trim();
}
}
}
return null;
}
这里需要对api接口重新处理使因为在搜索歌曲后,根据选择的歌曲不同,api接口中的参数需要进行调整才能返回这首歌曲的详细参数,其中的播放链接指向这首歌曲的下载地址,利用这个地址,便可以在线下载音乐。
3) 弹出提示,用户自行选择下载路径,选择后,调用DownloadSongAsync
方法去下载音乐。
if (!string.IsNullOrEmpty(downloadUrl))
{
string fileName = $"{Guid.NewGuid()}.flac"; // 用GUID作为文件名
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
saveFileDialog.FileName = fileName;
saveFileDialog.Filter = "FLAC files (*.flac)|*.flac|All files (*.*)|*.*";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
progressBar1.Value = 0; // 重置进度条
await DownloadSongAsync(downloadUrl, saveFileDialog.FileName); // 调用下载歌曲的异步方法
MessageBox.Show("下载完成");
}
}
}
else
{
MessageBox.Show("未找到播放链接");
}
4) 下载文件,并提示下载进度
private async Task DownloadSongAsync(string url, string filePath)
{
//设置最长响应时间:30min
using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromMinutes(30) })
{
// 获取响应流
using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{
//确保响应成功
response.EnsureSuccessStatusCode();
long? totalBytes = response.Content.Headers.ContentLength;
using (Stream contentStream = await response.Content.ReadAsStreamAsync(),
fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
{
var buffer = new byte[8192];
long totalReadBytes = 0;
int readBytes;
while ((readBytes = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileStream.WriteAsync(buffer, 0, readBytes);
totalReadBytes += readBytes;
if (totalBytes.HasValue)
{
// 更新进度条
progressBar1.Value = (int)((totalReadBytes * 100) / totalBytes.Value);
}
}
}
}
}
}
这里同样采取await
和 Task
设计,除此之外,此处利用response.Content.Headers.ContentLength
获取响应内容的总字节大小,并采取HTTP客户端->缓冲区->文件的流程下载文件,在下载中记录目前已经下载的字节数totalReadBytes
,实现下载进度条。
至此,整个在线下载音乐功能实现完毕,用户可以自己搜索关键词,下载音乐。
疑似咪咕音乐的资源不多,能选的有点少~
6.3 在线播放音乐
在线下载音乐功能可以分为三个模块实现:搜索歌曲、选择想要播放的歌曲、播放音乐,实现效果如下:
6.3.1 搜索歌曲 && 6.3.2 选择想要播放的歌曲
此处的实现思路和在线下载音乐的思路一样的,不在此展出了。
6.3.3 播放音乐
用户点击播放按钮,调用button3_Click
事件处理程序,先检查是否有选中的歌曲,并异步调用GetPlayUrlAsync
方法获取播放链接,将链接传入PlayAudio
函数中调用AxWindowsPlayer
控件实现在线播放功能:
button3_click
的代码实现如下:
private async void button3_Click(object sender, EventArgs e)
{
if (selectedIndex == -1 || selectedIndex >= responseLines.Count)
{
MessageBox.Show("请选择要播放的歌曲");
return;
}
string playUrl = await GetPlayUrlAsync(selectedIndex); // 调用获取播放链接的异步方法
if (!string.IsNullOrEmpty(playUrl))
{
PlayAudio(playUrl); // 调用播放音乐的方法
}
else
{
MessageBox.Show("未找到播放链接");
}
}
GetPlayUrlAsync
方法与前面的 GetDownloadUrlAsync
方法几乎一致,只改变了函数名,就不展示了~
PlayAudio
函数中调用AxWindowsPlayer
控件实现在线播放功能,在作业二中只使用了该控件的本地播放功能,其实它可以通过传递url参数实现在线播放功能。
不得不说,查文档大的时候才意识到,可能上课讲过,一下子记不得了,折腾了好久,原来的思路是使用
WebBrower
控件去实现,这样的缺点是,播放器是调用的本地自己的播放器,不太方便,故选用这种方式。
public void PlayAudio(string url)
{
axWindowsMediaPlayer1.URL = url;
axWindowsMediaPlayer1.Ctlcontrols.play();
}
针对在线播放功能,我也仿照前面本地播放功能实现了音量控制、暂停播放和下一首:
private async void button2_Click(object sender, EventArgs e)
{
if (responseLines.Count > 0)
{
int index = (selectedIndex + 1);
if (index >= responseLines.Count())
{
index = 0;
}
string playUrl = await GetPlayUrlAsync(index); // 调用获取播放链接的异步方法
PlayAudio(playUrl);
selectedIndex = index;
listBox1.SelectedIndex = index - 1;
}
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
}
private void button4_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.Ctlcontrols.stop(); // 停止播放
}
这里其实应该要像本地播放那样针对不同格式的音乐进行不同的处理,但是我这个API返回的好像是只有
MP4
格式,就让我偷个懒吧~
7. 设计难点分析
本次作业中我认为最难的应该是那个异步处理部分和文件下载部分,网络连接可以通过文档一步步调整,但是这个异步处理的思路,我最开始是没有想到的,后来还是问了ChatGPT才解决的,文件下载我最开始以为是直接可以一步到位下载到文件(因为我在网页中打开链接的时候是直接下载的),忽略了缓冲区,老是爆各种各样的错误。
8. 完整代码地址
C#实现音乐在线播放和下载——Windows程序设计作业3
9. 总结&改进思路
这次的项目设计本地的音乐播放和在线音乐的搜索、选择、下载和播放功能,总体上是一个较为完整的项目了。
尽管目前的功能已经能够满足基本的需求,但仍有许多改进和优化的空间。以下是一些可能的改进思路:
1) 增强用户体验:可以在搜索结果的基础上,增加分页、排序、过滤功能。
2) 提高代码可维护性:可以引入MVVM模式将业务和UI逻辑分离,提升代码的可读性。
3) 拓展功能:可以拓展用户功能,用户可以个性化搜索和收藏歌单,与其他人分享自己存在本地的歌单。
期待未来能够再次更新此项目,实现更多的功能~