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

Node.js中转Box文件下载:实现网络异常时的暂停与续传

Solution for Resumeable Box File Downloads via Node.js Proxy

Got it, let's work through how to add resumeable downloads and automatic retries to your Node.js proxy for Box files. The main issue with your current code is that it doesn't handle network drops or partial failures—once the connection breaks, the download stops cold and throws an error. Here's how to fix this properly:

1. First, Support HTTP Range Requests (The Foundation of Resumeable Downloads)

Browsers send a Range header when resuming a partial download, asking for bytes starting from a specific offset. Your proxy needs to:

  • Parse this header from the client's request
  • Forward the same Range header to Box
  • Respond with a 206 Partial Content status and the appropriate headers to confirm the range being sent

2. Add Retry Logic for Failed Box Requests

When the connection to Box drops or times out, we want to automatically retry the request (with a backoff delay to avoid overwhelming the server) instead of immediately failing the client's download.

3. Robust Stream Error Handling

Instead of ending the client's response on the first error, we'll catch stream errors, trigger a retry, and resume the stream once the connection is restored.

Modified Core Code

Here's the updated code implementing all these features:

const urlParse = require('url');
const https = require('https');

// Configure retry settings
const MAX_RETRIES = 5;
const RETRY_DELAY_BASE = 1000; // 1 second base delay

function handleBoxDownload(req, res, fileUrl, retryCount = 0) {
  const obj = urlParse.parse(fileUrl, true);
  // Build request options for Box, including Range header if present
  const options = {
    ...obj,
    headers: {
      ...(req.headers.range ? { Range: req.headers.range } : {})
    }
  };

  const creq = https.request(options, (cres) => {
    // Handle partial content or success
    if (cres.statusCode === 200 || cres.statusCode === 206) {
      res.writeHead(cres.statusCode, {
        'Content-Type': 'application/octet-stream',
        'Content-Disposition': cres.headers['content-disposition'],
        'Accept-Ranges': 'bytes', // Tell browser we support resuming
        ...(cres.headers['content-range'] ? { 'Content-Range': cres.headers['content-range'] } : {})
      });

      // Pipe the Box response to the client, with error handling
      const stream = cres.pipe(res);
      stream.on('error', (err) => {
        console.error(`Stream error: ${err.message}, retrying...`);
        retryDownload(req, res, fileUrl, retryCount);
      });
    } else {
      // Pass through non-success status codes to the client
      res.writeHead(cres.statusCode, { 'Content-Type': 'application/json' });
      cres.pipe(res);
    }
  });

  // Handle request errors (e.g., network timeout, connection reset)
  creq.on('error', (err) => {
    console.error(`Box request failed: ${err.message}, retry count: ${retryCount}`);
    retryDownload(req, res, fileUrl, retryCount);
  });

  // Set a timeout to avoid hanging requests
  creq.setTimeout(10000, () => {
    console.error('Request to Box timed out');
    creq.abort();
    retryDownload(req, res, fileUrl, retryCount);
  });

  creq.end();
}

function retryDownload(req, res, fileUrl, retryCount) {
  if (retryCount >= MAX_RETRIES) {
    // Max retries reached, send user-friendly error
    res.statusCode = 503;
    return res.end('Failed to complete download after multiple retries. Please try again later.');
  }

  // Calculate exponential backoff delay
  const delay = RETRY_DELAY_BASE * Math.pow(2, retryCount);
  setTimeout(() => {
    handleBoxDownload(req, res, fileUrl, retryCount + 1);
  }, delay);
}

// Example usage in your route handler
app.get('/', (req, res) => {
  const fileUrl = req.query.url;
  if (!fileUrl) {
    return res.status(400).end('Missing "url" query parameter');
  }
  handleBoxDownload(req, res, fileUrl);
});

Key Improvements Explained

  • Range Request Support: We check for the client's Range header and forward it to Box, then send back Accept-Ranges and Content-Range headers so the browser knows it can resume downloads.
  • Exponential Backoff Retries: When a request fails, we wait longer between retries (1s, 2s, 4s, etc.) to give the network time to recover, up to a maximum retry limit.
  • Stream & Timeout Handling: We added timeouts to avoid hanging requests, and handle stream errors by triggering a retry instead of immediately ending the client's response.
  • Graceful Failure: After max retries, we send a clear, user-friendly error instead of a generic network failure message.

Testing Tips

  • Use browser dev tools (Network tab) to simulate slow networks or manually interrupt downloads to verify resuming works.
  • Test with large files to ensure partial downloads can pick up where they left off.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:36:25