记一次去水印小程序遇到的downloadFile合法域名的问题和解决方案

413 阅读4分钟

先看成品:

35d28dfcf14b87eff989c5300ab03743

我是个抖音重度使用者,平时看到比较有意思或者让人思考的短视频,我总喜欢把他下载到手机相册中,然后分享给朋友。避免视频失效,而且不消耗流量。但是有些视频无法进行下载,加上下载后视频有水印,这在一定程度上影响了我视频的观看体验。

虽然去水印工具很早就出来了,但是使用时都感觉不尽如人意。保存视频需要观看广告,打开小程序弹窗广告满天飞,界面设计过于冗余,这让我觉得很难用。于是,就有了自己开发小程序的想法。

一切都进展很顺利,小程序主要功能也已经开发完成。就在我准备上架的时候,出现了一个问题,无水印视频无法下载

如图所示

image-20250307211356469

查阅资料得知,小程序想要下载文件,必须要去微信公众平台: 链接,小程序后台添加downloadfile合法域名列表才可以。我刚开始,添加了合法域名列表,认为事情完美解决了,就把小程序给匆匆上线了。哪知道 刚过十分钟,用户就反馈视频无法下载,我心想:“不应该啊,这个问题不是解决了吗?”,怎么还有问题呢?

表情包丨我倒要看看怎么个事儿_手机搜狐网

通过上网查询得知,抖音服务器众多,保存视频的服务器不固定,也就是说无水印视频的域名是不固定的,而且数目不少,我一想,这完了,这基本不可能把白名单做到全覆盖啊。我就开始在网上查资料,功夫不负有心人-----发现可以通过代理来解决这个问题。

image-20250307213732671

如图所示,于是我就开始改造项目

完成这项工作,你需要:

  1. 一个已经备案过的域名
  2. 一台服务器

服务器配置步骤:

  1. 进入服务器管理面板,点击「网站」→「添加站点」
  2. 选择「传统项目」→「域名」
  3. 填写域名信息并保存

image-20250307214400079

填写完成之后,去网站根目录,上传php代理文件

image-20250307214631118

php代理文件代码如下:

 <?php
 // 开启错误报告
 error_reporting(E_ALL);
 ini_set('display_errors', 1);
 ​
 // 定义日志文件路径
 define('LOG_FILE', __DIR__ . '/download_log.txt');
 ​
 // 日志函数
 function writeLog($message) {
     $timestamp = date('Y-m-d H:i:s');
     $logMessage = "[$timestamp] $message\n";
     
     // 尝试写入日志
     $result = @file_put_contents(LOG_FILE, $logMessage, FILE_APPEND);
     
     // 如果写入失败,输出错误信息
     if ($result === false) {
         $error = error_get_last();
         header('Content-Type: application/json');
         echo json_encode([
             'error' => 'Failed to write log',
             'details' => $error,
             'file_exists' => file_exists(LOG_FILE),
             'is_writable' => is_writable(LOG_FILE),
             'path' => LOG_FILE
         ]);
         exit;
     }
 }
 ​
 // 测试日志写入
 writeLog('=== Starting download proxy service ===');
 ​
 // 记录请求信息
 writeLog('Request URL: ' . ($_GET['url'] ?? 'No URL provided'));
 writeLog('User Agent: ' . ($_SERVER['HTTP_USER_AGENT'] ?? 'No User Agent'));
 ​
 // 设置CORS头
 header('Access-Control-Allow-Origin: *');
 header('Access-Control-Allow-Methods: GET');
 header('Access-Control-Allow-Headers: Content-Type');
 ​
 // 检查URL参数
 if (!isset($_GET['url'])) {
     writeLog('Error: No URL parameter provided');
     header('HTTP/1.1 400 Bad Request');
     echo json_encode(['error' => 'URL parameter is required']);
     exit;
 }
 ​
 $url = $_GET['url'];
 writeLog("Processing URL: $url");
 ​
 // 初始化CURL
 $ch = curl_init();
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 curl_setopt($ch, CURLOPT_HEADER, true);
 curl_setopt($ch, CURLOPT_NOBODY, true);
 ​
 // 获取头信息
 writeLog('Fetching headers...');
 $result = curl_exec($ch);
 $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
 $headers = substr($result, 0, $headerSize);
 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 writeLog("Response HTTP Code: $httpCode");
 writeLog("Response Headers: $headers");
 ​
 if ($httpCode !== 200) {
     writeLog("Error: HTTP Code $httpCode");
     header('HTTP/1.1 ' . $httpCode);
     echo json_encode(['error' => 'Failed to fetch resource', 'code' => $httpCode]);
     exit;
 }
 ​
 // 重新设置CURL获取完整内容
 curl_setopt($ch, CURLOPT_NOBODY, false);
 $result = curl_exec($ch);
 ​
 if ($result === false) {
     $error = curl_error($ch);
     writeLog("CURL Error: $error");
     header('HTTP/1.1 500 Internal Server Error');
     echo json_encode(['error' => 'CURL error', 'details' => $error]);
     exit;
 }
 ​
 // 获取内容类型
 $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
 writeLog("Content Type: $contentType");
 ​
 // 设置响应头
 header('Content-Type: ' . $contentType);
 echo $result;
 ​
 // 记录成功
 writeLog('Download completed successfully');
 ​
 curl_close($ch);
 ?>

编写好代理文件后,如图操作,重启nginx

image-20250307214838569

这个时候还不行,小程序端还没有进行相应的处理

小程序代码如下

   // 构建代理URL
   buildProxyUrl(originalUrl) {
     return `https://你的域名/download.php?url=${encodeURIComponent(originalUrl)}`;
   },
    const downloadTask = wx.downloadFile({
       url: videoUrl,
       success: (res) => {
         console.log('直接下载结果:', res);
         if (res.statusCode === 200 || res.statusCode === 206) {
           this.saveToAlbum(res.tempFilePath);
         } else {
           // 直接下载失败,尝试使用代理
           console.log('直接下载失败,尝试代理下载');
           const proxyUrl = this.buildProxyUrl(videoUrl);
           
           wx.downloadFile({
             url: proxyUrl,
             success: (proxyRes) => {
               console.log('代理下载结果:', proxyRes);
               if (proxyRes.statusCode === 200 || proxyRes.statusCode === 206) {
                 this.saveToAlbum(proxyRes.tempFilePath);
               } else {
                 wx.hideLoading();
                 this.handleDownloadError(videoUrl, '下载失败,是否复制链接到浏览器下载?');
               }
             },
             fail: (err) => {
               console.error('代理下载失败:', err);
               wx.hideLoading();
               this.handleDownloadError(videoUrl, '下载失败,是否复制链接到浏览器下载?');
             }
           }).onProgressUpdate((res) => {
             wx.showLoading({
               title: `已下载 ${res.progress}%`,
               mask: true
             });
           });
         }
       },
       fail: (err) => {
         console.error('直接下载失败:', err);
         if (err.errMsg && err.errMsg.includes('url not in domain list')) {
           // 域名不在白名单,使用代理下载
           console.log('使用代理下载');
           const proxyUrl = this.buildProxyUrl(videoUrl);
           
           wx.downloadFile({
             url: proxyUrl,
             success: (proxyRes) => {
               console.log('代理下载结果:', proxyRes);
               if (proxyRes.statusCode === 200 || proxyRes.statusCode === 206) {
                 this.saveToAlbum(proxyRes.tempFilePath);
               } else {
                 wx.hideLoading();
                 this.handleDownloadError(videoUrl, '下载失败,是否复制链接到浏览器下载?');
               }
             },
             fail: (proxyErr) => {
               console.error('代理下载失败:', proxyErr);
               wx.hideLoading();
               this.handleDownloadError(videoUrl, '下载失败,是否复制链接到浏览器下载?');
             }
           }).onProgressUpdate((res) => {
             wx.showLoading({
               title: `已下载 ${res.progress}%`,
               mask: true
             });
           });
         } else {
           wx.hideLoading();
           this.handleDownloadError(videoUrl, '下载遇到问题,是否复制链接到浏览器下载?');
         }
       }
     });

到目前为止,都已经完成,还差最后一步,微信公众平台的域名只支持https模式,所以必须要申请ssl证书

步骤如图:

image-20250307215733906

申请完成后,打开微信公众平台 链接

如图操作即可

image-20250307220036585

之后再下载无水印视频的时候,会通过代理进行下载,也不会出现下载失败的问题。

如果你还有其他问题,欢迎留言