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

Handlebars模板设置src后HTML5 audio无法播放问题求助

Fixing Audio Stream Playback with GridFS and Handlebars

Let's break down what's causing your audio player issues and walk through the fixes step by step.

Core Issues in Your Current Code

  1. Conflicting Responses: Your /play route is trying to do two mutually exclusive things: pipe the audio stream to the response and render the Handlebars page. Once you call downloadStream.pipe(res), the HTTP response is already sent to the browser—any subsequent res.render calls will cause invalid, mixed content that the audio tag can't parse.
  2. Static Source URL: You're trying to set the audio tag's src on page load, but the /play route is meant to serve a specific track's stream, not a generic source that works for all tracks.

Step-by-Step Fixes

1. Separate Routes for Page Rendering and Audio Streaming

First, split your logic into two distinct routes: one to serve the track list page, and another exclusively for streaming audio.

Update your index.js:

// Route to render the track list and audio player
app.get('/list', urlEditor, function(req, res, next) {
  const db = req.db;
  const username = 'ross';

  if (!username) {
    return res.status(400).send('Username not configured');
  }

  const collection = db.collection(`${username}.files`);
  const userNameId = collection.s.name;

  collection.find({}).toArray(function(err, tracks) {
    if (err) return next(err);
    // Render the page without pre-setting the audio source
    res.render("list", { 
      tracks: tracks, 
      userNameId: userNameId 
    });
  });
});

// Route exclusively for streaming audio from GridFS
app.get('/play', urlEditor, function(req, res, next) {
  const db = req.db;
  const size = sanitizer.escape(req.query.bytes);
  const sanitizedTrackId = sanitizer.escape(req.query.id);
  const username = 'ross';

  // Validate track ID
  try {
    const trackID = new ObjectID(sanitizedTrackId);
  } catch (err) {
    return res.status(400).json({ 
      message: "Invalid trackID in URL parameter. Must be a single String of 12 bytes or a string of 24 hex characters" 
    });
  }

  const playEngine = new mongo.GridFSBucket(db, { bucketName: username });
  const downloadStream = playEngine.openDownloadStream(trackID);

  // Set proper audio headers to ensure the browser recognizes it as audio
  res.set({
    'Content-Type': 'audio/mpeg',
    'Content-Length': size,
    'Accept-Ranges': 'bytes'
  });

  // Pipe the audio stream to the response (this completes the HTTP response)
  downloadStream.pipe(res);

  // Handle stream errors gracefully
  downloadStream.on('error', function(err) {
    console.error('Stream error:', err);
    res.status(500).send('Failed to stream audio');
  });
});

2. Update Handlebars Template to Dynamically Set Audio Source

Instead of hardcoding the src on page load, use JavaScript to set the audio source when a user clicks a play button. This ensures the audio tag gets the correct stream URL for the selected track.

Update list.handlebars:

<!DOCTYPE html>
<html>
<head>
  <title>CRATE</title>
</head>
<body>
  <div>tracks displayed below</div>
  <div>{{userNameId}}'s Tracks</div>
  
  {{#each tracks}}
  <div class="row">
    <div class="col-md-12">
      <!-- Use data attributes to store track ID and size -->
      <button class="playButton" data-track-id="{{_id}}" data-track-bytes="{{length}}">
        {{filename}}
      </button>
    </div>
  </div>
  {{/each}}

  <audio id="playback" type="audio/mpeg" controls>
    Your browser does not support the HTML5 audio element.
  </audio>

  <script>
    // Get reference to the audio player
    const audioPlayer = document.getElementById('playback');
    
    // Add click handlers to all play buttons
    document.querySelectorAll('.playButton').forEach(button => {
      button.addEventListener('click', (e) => {
        e.preventDefault();
        
        // Get track data from button attributes
        const trackId = button.dataset.trackId;
        const trackBytes = button.dataset.trackBytes;
        
        // Build the stream URL with proper encoding
        const streamUrl = `/play?id=${encodeURIComponent(trackId)}&bytes=${encodeURIComponent(trackBytes)}`;
        
        // Update the audio source and trigger playback
        audioPlayer.src = streamUrl;
        audioPlayer.play().catch(err => console.error('Playback error:', err));
      });
    });
  </script>
</body>
</html>

Why This Works

  • Separate Responsibilities: The /list route handles page rendering, and /play handles only audio streaming—no conflicting responses.
  • Dynamic Source: The audio tag's src is set only when a track is selected, ensuring it points to the correct stream for that track.
  • Proper Headers: Setting Content-Type: audio/mpeg tells the browser explicitly to treat the stream as audio, so it won't fall back to a video player.

Additional Notes

  • Verify that your GridFS files are valid MP3s—corrupted files can cause playback issues even with correct code.
  • Test the /play route directly in your browser first to confirm the stream works before integrating it with the audio tag.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:35:16