Django案例 - 音樂網站實現
因為之前的雲端儲存課程,要做一個跟雲端儲存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模組,結合了[郵件](# ofollow,noindex">https://docs.djangoproject.com/en/2.0/topics/email/ )模組,利用ajax動態載入資料,實現即時傳遞以及資料校驗,使用者模組的話是可以在admin後臺看到資訊的,進行新增刪除,郵件設定的時候校驗碼和郵箱型別設定得注意,按照官網模板進行設定就好了,這一塊的功能無非就是註冊登入,密碼修改、郵箱驗證、名字修改、忘記密碼,退出登入這一些,都比較簡單,簡單展示下基礎實現的程式碼,更多涉及資料校驗之類的詳情參考 原始碼

image.png

image.png
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