因为之前的云存储课程,要做一个跟云存储oss相关的案例,因为正好那会儿在学Django,所以首先就想到了用这个来实现一个网站,有想过什么第三方库啊之类的,因为之前有听说过什么七牛云,还有将静态文件压缩保存之类的,之前实验室的上司也曾经有项目涉及到用亚马逊云保存资源的案例,所以就有了我们现在的这个项目,主要是用Django实现一个音乐网站,就不涉及到数据库和模型之类的了,充其量只有用户模块会稍稍涉及一点模型,不过数据很小,占用资源也不多,之后会想其它方法来解决
前端页面主要是用HTML+js来实现,歌词、歌单也是从js文件里面直接获取,主要就是用Django实现数据传递和动态加载
文件结构
直接用tree生成的,没有略去无关文件,主要是让大家简单了解下所用到的模块和文件结构
卷 F 的文件夹 PATH 列表
卷序列号为 0F02-10B8
F:.
│ db.sqlite3
│ manage.py
│ settings.py
│ tree.txt
│ urls.py
│ wsgi.py
│ __init__.py
│
├─.idea
│ dataSources.local.xml
│ dataSources.xml
│ misc.xml
│ modules.xml
│ oss_test1.iml
│ workspace.xml
│
├─file
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ test2.py
│ │ tests.py
│ │ urls.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─migrations
│ │ │ __init__.py
│ │ │
│ │ └─__pycache__
│ │ __init__.cpython-36.pyc
│ │
│ ├─templatetags
│ │ │ url_tag.py
│ │ │ __init__.py
│ │ │
│ │ └─__pycache__
│ │ url_tag.cpython-36.pyc
│ │ __init__.cpython-36.pyc
│ │
│ └─__pycache__
│ admin.cpython-36.pyc
│ models.cpython-36.pyc
│ urls.cpython-36.pyc
│ views.cpython-36.pyc
│ __init__.cpython-36.pyc
│
├─media
│ └─upload
│ └─2018
│ └─02
│ └─05
│ lwpbrq.png
│
├─oss_test1
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ │ __init__.py
│ │
│ └─__pycache__
│ settings.cpython-36.pyc
│ urls.cpython-36.pyc
│ wsgi.cpython-36.pyc
│ __init__.cpython-36.pyc
│
├─search
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ urls.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─migrations
│ │ │ __init__.py
│ │ │
│ │ └─__pycache__
│ │ __init__.cpython-36.pyc
│ │
│ └─__pycache__
│ admin.cpython-36.pyc
│ models.cpython-36.pyc
│ urls.cpython-36.pyc
│ views.cpython-36.pyc
│ __init__.cpython-36.pyc
│
├─static
│ │ base.css
│ │ home.css
│ │ jquery-1.12.4.min.js
│ │
│ ├─bootstrap-3.3.7
│ │ ├─css
│ │ │ bootstrap.min.css
│ │ │ bootstrap.min.css.map
│ │ │
│ │ ├─fonts
│ │ │ glyphicons-halflings-regular.eot
│ │ │ glyphicons-halflings-regular.svg
│ │ │ glyphicons-halflings-regular.ttf
│ │ │ glyphicons-halflings-regular.woff
│ │ │ glyphicons-halflings-regular.woff2
│ │ │
│ │ └─js
│ │ bootstrap.min.js
│ │
│ ├─cloud
│ │ 黄耀明 - 暗涌(Live) - live.mp3
│ │
│ ├─css
│ │ │ base.css
│ │ │ bootstrap.css.map
│ │ │ bootstrap.min.css
│ │ │ bootstrap.min.css.map
│ │ │ bulma.min.css
│ │ │ index.css
│ │ │ music.css
│ │ │
│ │ └─img
│ │ 1.jpg
│ │ alin.jpg
│ │ disk.png
│ │ dknz.jpg
│ │ hym.jpg
│ │ js.jpg
│ │ lf.jpg
│ │ lkq.jpg
│ │ lry.jpg
│ │ lzs.jpg
│ │ play_btn_next.png
│ │ play_btn_prev.png
│ │ play_icn_loop.png
│ │ play_icn_src.png
│ │ play_rdi_btn_pause.png
│ │ play_rdi_btn_play.png
│ │ process_btn.png
│ │ song_play_icon.png
│ │ tofirstpage.jpg
│ │ xg.jpg
│ │ xuruyun.jpg
│ │ zhl.jpg
│ │ zjx.jpg
│ │
│ ├─fonts
│ │ glyphicons-halflings-regular.eot
│ │ glyphicons-halflings-regular.svg
│ │ glyphicons-halflings-regular.ttf
│ │ glyphicons-halflings-regular.woff
│ │ glyphicons-halflings-regular.woff2
│ │
│ ├─img
│ │ alin.jpg
│ │ disk.png
│ │ dknz.jpg
│ │ download.png
│ │ file.png
│ │ hym.jpg
│ │ img.jpg
│ │ js.jpg
│ │ lf.jpg
│ │ lkq.jpg
│ │ lry.jpg
│ │ lzs_.jpg
│ │ play_disc.png
│ │ play_needle.png
│ │ search.png
│ │ timg.jpg
│ │ upload.png
│ │ xg.jpg
│ │ xuruyun.jpg
│ │ zhl.jpg
│ │ zjx.jpg
│ │
│ ├─js
│ │ index.js
│ │ jquery-min.js
│ │ lyrics.js
│ │ move.js
│ │ music.js
│ │
│ └─media
├─templates
│ │ base.html
│ │ error.html
│ │ form.html
│ │ index.html
│ │ index2.html
│ │
│ ├─file
│ │ detail.html
│ │ list.html
│ │ search_detail.html
│ │ search_list.html
│ │
│ └─user
│ bind_email.html
│ forgot_password.html
│ login.html
│ register.html
│ user_info.html
│
├─user
│ │ admin.py
│ │ context_processors.py
│ │ forms.py
│ │ models.py
│ │ urls.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─migrations
│ │ │ 0001_initial.py
│ │ │ 0002_auto_20181121_1311.py
│ │ │ __init__.py
│ │ │
│ │ └─__pycache__
│ │
│ │
│ └─__pycache__
│
│
└─__pycache__
主页
页面全都是用bootstrap框架做的简单样例,基础(base.html)模板中定义了播放器主页面,上方导航栏定义的是用户模块,包括搜索、上传、下载和用户登录注册模块,如下图(开场动画我就略过了,播放器代码是在github上down的某位大佬的,简单修改了下)
image.png
主页主要是是实现了下js数据的传递,播放器本来是从一个专门存储歌词、歌单的js文件里面读取数据,现在我将歌词和歌曲都放在了云服务器的对象存储桶(oss)里面,所以就直接查询数据将它返回到主页,js数据直传详情参考我之前的文章,这里因为我的服务器带宽等一系列条件限制,大量数据处理会反应较慢,这里就只是简单对歌单文件进行了处理,歌词和图片我并没有全部上传(数据太多爆桶要加钱呀QAQ),但是处理方法都是一样的,这里就仅用歌单做个演示,其他数据使用默认,oss的处理也可以参考我前面的内容
def index(request):
base_url = "https://maya-music.oss-cn-beijing.aliyuncs.com/"
files_name = []
url_list = []
data = []
# 打印出云端每个文件的名字,将名字存入files_name列表
for i in oss2.ObjectIterator(bucket):
files_name.append(i.key[:-4])
# 拼接出所有链接,存入url_list字典
for name in files_name:
url_list.append(base_url + name + '.mp3')
for song, src in zip(files_name, url_list):
data.append({'song': song, 'src': src, 'singer': "黄耀明", 'img': 'static/css/img/hym.jpg', 'lyric': 'anyong'})
return render(request, 'index.html', {'list': json.dumps(data, ensure_ascii=False)})
上传功能
在导航栏内添加了bootstrap表单可以实现文件定位,然后Django获取的文件对象,利用自带的文件处理模块,将文件下载至本地,然后用oss模块处理上传至服务器,最后利用chdir、remove等方法将本地文件删除即实现了一个“假上传功能”,只是个用来熟悉Django的小练习嘛不必较真
def upload_file(request):
referer = request.META.get('HTTP_REFERER', reverse('home'))
if request.method == "POST": # 请求方法为POST时,进行处理
myFile =request.FILES.get("myfile", None) # 获取上传的文件,如果没有文件,则默认为None
if not myFile:
return HttpResponse("no files for upload!")
destination = open(os.path.join(dir_path, myFile.name), 'wb+') # 打开特定的文件进行二进制的写操作
for chunk in myFile.chunks(): # 分块写入文件
destination.write(chunk)
destination.close()
with open(oss2.to_unicode(dir_path+"/"+myFile.name), 'rb') as f:
bucket.put_object(myFile.name, f)
temp_path = ".\static"
os.chdir(os.path.abspath(temp_path))
shutil.rmtree("media")
os.mkdir("media")
os.chdir(now_pwd)
return redirect(request.GET.get('from', reverse('home')))
下载功能
原理同上,同样是下载至本地然后实现本地下载,最后删除文件,但是在处理方式上会有些不同,因为没有设计models,所以这里无法定位到具体的歌曲对象,只能实现单首歌曲下载,我的html里面的audio恰好是获取歌单对象,动态实现切换歌曲源,所以audio里面的src恰好就是当前歌曲的url,这里就钻了个空子,直接用requests+BeautufulSoup获取到audio下的src,再将数据转递过去,根据src在远端服务器进行匹配,获取到对应url后进行下载,这里oss无法处理url里面的中文字符,涉及到url转码,可以参考我之前的相应版块的介绍
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()#状态码为200则返回文本否则抛出异常
r.encoding = 'utf-8'
# r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
def get_data(html):
src = BeautifulSoup(html, 'html.parser', from_encoding='utf-8').find('audio', {'id': 'audio'}).get('src')
return src
def download_file(request):
referer = request.META.get('HTTP_REFERER', reverse('home'))
url = "http://127.0.0.1:8000/"
html = getHTMLText(url)
src = get_data(html)
file_name = unquote(src, 'utf-8').split('com/')[1]
temp_path2 = ".\static\cloud"
os.chdir(os.path.abspath(temp_path2))
print("***************")
print("cloud:", os.getcwd())
bucket.get_object_to_file(file_name, file_name)
file = open(file_name, 'rb')
response = FileResponse(file)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="music.mp3"'
# file.close()
# os.remove(file_name)
os.chdir(now_pwd)
return response
# redirect(request.GET.get('from', reverse('home')))
搜索功能
搜索功能因为没有设计models的原因,也没有办法用自带的搜索模块了(模糊查询),这里就只能自己设计,主要是根据表单内传入的字符串去远端服务器内进行匹配,因为服务器内文件的url是根据名字生成的,会包含文件名,所以可以根据名字进行查询,然后返回结果集,将数据返回到新的展示页面,页面就简单展示了下数据,并没有特殊设计,同样继承自base.html
image.png
def get_filesname():
files_name = [] # 歌曲名字
for i in oss2.ObjectIterator(bucket):
files_name.append(i.key[:-4])
return files_name
def get_picname(list_):
pic_name = [] # 图片全称
for name in list_:
pic_name.append("https://maya-picture.oss-cn-beijing.aliyuncs.com/"+name+".jpg")
return pic_name
def get_singer(list_):
singers = [] # 歌手名字
for name in list_:
s_name = name.split(' - ')[0]
singers.append(s_name)
return singers
def search(request):
search_page = request.POST.get('name')
# search_page = input("请输入")
search_song = []
search_singer = []
context = {}
files_name = get_filesname()
for name in files_name:
if search_page in name:
search_song.append(name)
search_singer = get_singer(search_song)
search_pic = get_picname(search_song)
context['search_song'] = search_song
context['search_singer'] = search_singer
context['search_pic'] = search_pic
context['result'] = list(zip(search_song, search_singer, search_pic))
context['src'] = search_song
for x,y,z in zip(context['search_song'],context['search_singer'],context['search_pic']):
print("song:%s singer:%s pic:%s" %(x,y,z))
return render(request, 'file/search_list.html', context)
歌曲详情页
这里本来是除了展示歌曲详情信息,另外还想增加歌单模块、评论模块的,但是依然是苦于models的折磨,就只能暂时实现了一个无后台的纯前端评论块,没错,纯前台,使用的大佬们的第三方接口,承蒙分享,其他的基本跟上一个板块类似,数据传递,返回模板
image.png
def detail(request, src):
## src == 歌曲名字
song_name = []
singer_name = []
pic_src = []
song_name.append(src)
singer_name.append(src.split(' - ')[0])
pic_src.append("https://maya-picture.oss-cn-beijing.aliyuncs.com/"+src+".jpg")
context = {}
context['result'] = list(zip(song_name,singer_name,pic_src))
print("song_name:%s,singer:%s,pic:%s" %(song_name, singer_name, pic_src))
return render(request, 'file/search_detail.html', context)
用户模块
注册和登录功能参考的都是Django自带的auth模块,结合了[邮件](# https://docs.djangoproject.com/en/2.0/topics/email/)模块,利用ajax动态加载数据,实现即时传递以及数据校验,用户模块的话是可以在admin后台看到信息的,进行添加删除,邮件设置的时候校验码和邮箱类型设置得注意,按照官网模板进行设置就好了,这一块的功能无非就是注册登录,密码修改、邮箱验证、名字修改、忘记密码,退出登录这一些,都比较简单,简单展示下基础实现的代码,更多涉及数据校验之类的详情参考源码
def login(request):
if request.method == 'POST':
login_form = LoginForm(request.POST)
if login_form.is_valid():
user = login_form.cleaned_data['user']
auth.login(request, user)
return redirect(request.GET.get('from', reverse('home')))
else:
login_form = LoginForm()
context = {}
context['login_form'] = login_form
return render(request, 'user/login.html', context)
def register(request):
if request.method == 'POST':
reg_form = RegForm(request.POST, request=request)
if reg_form.is_valid():
username = reg_form.cleaned_data['username']
email = reg_form.cleaned_data['email']
password = reg_form.cleaned_data['password']
# 创建用户
user = User.objects.create_user(username, email, password)
user.save()
# 清除session
del request.session['register_code']
# 登录用户
user = auth.authenticate(username=username, password=password)
auth.login(request, user)
return redirect(request.GET.get('from', reverse('home')))
else:
reg_form = RegForm()
context = {}
context['reg_form'] = reg_form
return render(request, 'user/register.html', context)
def logout(request):
auth.logout(request)
return redirect(request.GET.get('from', reverse('home')))
def user_info(request):
context = {}
return render(request, 'user/user_info.html', context)
总结
项目难点确实不大,主要是熟悉Django以及oss的工作流程,借此也熟悉了很多python的模块,诸如上传下载还有os模块,还有对字符串的处理,列表、字典、元组的转换传递,这里只是简单展示介绍了实现流程以及基础代码,用户模块以及文件处理里面也有涉及到自定义标签、数据校验、Django表单、文件和邮件处理,完整代码可以参考我的Github
网友评论