You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django+PostgreSQL音乐播放器MP3上传提示不支持文件的解决求助

Django + PostgreSQL音乐播放器MP3上传报错修复

我正在使用Django和PostgreSQL开发音乐播放器,Song模型在数据库中可正常运行,但上传.mp3格式音频文件时,会提示“不支持的文件类型”错误。以下是相关代码文件及解决方法:

相关代码文件

models.py

from django.db import models
from django.contrib.auth.models import User
from cloudinary.models import CloudinaryField
from recordroomapp.helper import get_audio_length
from .validators import validate_is_audio


class Artist(models.Model):
    artist = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    created_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.artist


class Song(models.Model):
    title = models.CharField(max_length=100, unique=False)
    slug = models.SlugField(max_length=100, unique=True)
    song = models.FileField(
        upload_to='media/', validators=[validate_is_audio], null=True)
    song_length = models.DecimalField(
         blank=True, max_digits=10, decimal_places=2, null=True)
    songinfo = models.ForeignKey(
        "Artist", on_delete=models.CASCADE, related_name='song_post', null=True)
    updated_on = models.DateTimeField(auto_now=True)
    # featured_image = CloudinaryField('image', default='placeholder')
    created_on = models.DateTimeField(auto_now_add=True)
    likes = models.ManyToManyField(User, related_name='song_likes', blank=True)

    class Meta:
        ordering = ['-created_on']

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.song_length:
             audio_length = get_audio_length(self.song)
            self.song_length = f'{audio_length:.2f}'

        return super().save(*args, **kwargs)

    def number_of_likes(self):
        return self.likes.count()


class Comment(models.Model):
    post = models.ForeignKey(
        Song, on_delete=models.CASCADE, related_name='comments')
    name = models.CharField(max_length=100)
    email = models.EmailField()
    body = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    approved = models.BooleanField(default=False)

    class Meta:
        ordering = ['created_on']

    def __str__(self):
         return f"Comment {self.body} by {self.name}"

views.py

from django.shortcuts import render, get_object_or_404, HttpResponse
from django.views import generic, View
from .models import Song


class SongList(generic.ListView):
    model = Song
    queryset = Song.objects.order_by('-created_on')
    template_name = 'index.html'


class SongDetail(View):

    def get(self, request, slug, *args, **kwargs):
        queryset = Song.objects.all()
        post = get_object_or_404(queryset, slug=slug)
        comments = post.comments.filter(approved=True).order_by('-created_on')
        liked = False
        if post.likes.filter(id=self.request.user.id).exists():
            liked = True

        return render(
            request,
            "song_detail.html",
            {
                "song": queryset,
                "post": post,
                "comments": comments,
                "liked": liked
            },
        )

admin.py

from django.contrib import admin
from .models import Artist, Song, Comment
from django_summernote.admin import SummernoteModelAdmin


@admin.register(Artist)
class ArtistAdmin(SummernoteModelAdmin):
    list_display = ('artist', 'slug', 'created_on')
    search_fields = ['artist']


@admin.register(Song)
class SongAdmin(SummernoteModelAdmin):
    prepopulated_fields = {'slug': ('title',)}
    list_filter = ['created_on']
    summernote_fields = ('content')
    list_display = ('title', 'slug', 'song', 'created_on')
    search_fields = ['title', 'content']

validators.py(原问题代码)

import os
from django.core.exceptions import ValidationError
from mutagen.mp3 import MP3


def validate_is_audio(file):

    try:
        audio = MP3(file)

        if not audio:
            raise TypeError()

        first_file_check = True

    except Exception as e:
        first_file_check = False

    if not first_file_check:
        raise ValidationError('Unsupported file type.')
    valid_file_extensions = ('.mp3')
    ext = os.path.splitext(file.name)[1]
    if ext.lower() not in valid_file_extensions:
        raise ValidationError('Unacceptable file extension.')

helper.py

from mutagen.mp3 import MP3


def get_audio_length(file):
    audio = MP3(file)
    return audio.info.length

问题修复方案

1. 修正文件扩展名验证的元组定义

原validators.py中valid_file_extensions = ('.mp3')实际是字符串而非元组,导致ext.lower() not in valid_file_extensions判断逻辑失效。需改为:

valid_file_extensions = ('.mp3',)

2. 重置文件指针位置

使用MP3(file)读取文件后,文件指针会移动到末尾,后续操作(如save方法中调用get_audio_length)会无法读取文件,需要在读取后重置指针到起始位置。

修复后的validators.py

import os
from django.core.exceptions import ValidationError
from mutagen.mp3 import MP3

def validate_is_audio(file):
    first_file_check = True
    try:
        audio = MP3(file)
        # 重置文件指针到起始位置
        file.seek(0)
        if not audio:
            raise TypeError()
    except Exception as e:
        first_file_check = False

    if not first_file_check:
        raise ValidationError('Unsupported file type.')
    
    valid_file_extensions = ('.mp3',)
    ext = os.path.splitext(file.name)[1]
    if ext.lower() not in valid_file_extensions:
        raise ValidationError('Unacceptable file extension.')

修复后的helper.py

from mutagen.mp3 import MP3

def get_audio_length(file):
    audio = MP3(file)
    # 重置文件指针,避免后续上传失败
    file.seek(0)
    return audio.info.length

3. 可选:优化save方法的异常处理

如果上传文件为空,get_audio_length会报错,建议在save方法中添加非空判断:

def save(self, *args, **kwargs):
    if self.song and not self.song_length:
         audio_length = get_audio_length(self.song)
        self.song_length = f'{audio_length:.2f}'

    return super().save(*args, **kwargs)

内容的提问来源于stack exchange,提问作者Fintan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:05:34