为什么我在后续下载特定文件时会在 React Native 中收到 HTTP 412 状态错误?

Why am I receiving an HTTP 412 status error in React Native on subsequent downloads of a specific file?

我创建了一个 React Native 应用程序,它允许我将 Zip 包下载到我的应用程序中。在大多数情况下它都有效,我可以下载 zip 包,删除 zip 包,然后重新下载。但是,对于我的一个 zip 文件,我遇到了以下问题:

  1. 我下载安装zip包
  2. 我删除压缩包
  3. 我尝试再次下载,但这次出现 412 错误。

无论我尝试重新下载多少次,它都不会让我。为了克服这个问题,我执行以下操作:

  1. 用其他 zip 文件之一替换服务器上的 zip 文件
  2. 现在可以下载了
  3. 我从应用程序中删除包
  4. 我把服务器上的zip文件替换成原来的
  5. 我现在可以下载压缩包了

这一次有效,但如果我删除它并尝试再次安装,那么它又不会让我。

我也尝试过另一种方法,在这种情况下,我用有问题的 zip 文件替换了其他 zip 文件之一。同样,它允许我下载一次,但如果我想在以后下载它,它就不让我下载。

为什么这个 zip 文件会出现这个问题? zip文件都是用同样的方法制作的,第一次下载似乎没有问题。

起初我以为这是一个缓存问题,但为什么问题只与这个特定文件有关?

zip 文件是通过 PHP 以编程方式创建的,并与 Apache 一起提供。但是,我也从命令提示符手动创建它,问题仍然存在。

我的代码:

let response = await RNFetchBlob.config({
  path: temp_file,
})
  .fetch(
    "POST",
    "https://example.com/apptools/getZipPack.php",
    {
      "Accept": "application/json",
      "Content-Type": "application/json",
    },
    JSON.stringify({
      passcode: passcode,
      group_id: group_id,
      file: group.zp,
    })
  )
  .progress((received, total) => {
    console.log("progress", received / total);
    this.setState({
      progress_num: (received / total) * 100,
    });
  });

我收到的回复:

{"array": [Function anonymous], "base64": [Function anonymous], "blob": [Function anonymous], "data": "/Users/MyUserName/Library/Developer/CoreSimulator/Devices/940349C9-94F7-4EC7-95AC-31034876935D/data/Containers/Data/Application/C79E45D8-4D56-4BF9-9C2B-8846C58DE4F9/Documents/temp/45.zip", "flush": [Function anonymous], "info": [Function anonymous], "json": [Function anonymous], "path": [Function anonymous], "readFile": [Function anonymous], "readStream": [Function anonymous], "respInfo": {"headers": {"Connection": "Keep-Alive", "Content-Disposition": "attachment; filename="45_1.0.8_105.zip"", "Content-Length": "0", "Content-Type": "application/octet-stream", "Date": "Thu, 17 Sep 2020 19:28:09 GMT", "Etag": ""75cf8-5af85fff34576"", "Keep-Alive": "timeout=3, max=100", "Last-Modified": "Thu, 17 Sep 2020 17:49:03 GMT", "Server": "Apache/2.4.25 (Debian)"}, "redirects": ["https://example.com/apptools/getZipPack.php"], "respType": "blob", "rnfbEncode": "path", "state": "2", "status": 412, "taskId": "k4zitpcob29hhrzk245n5n", "timeout": false}, "session": [Function anonymous], "taskId": "k4zitpcob29hhrzk245n5n", "text": [Function anonymous], "type": "path"}

请注意内容长度为 0,状态为 412。

我为它创建的目录中放置了一个 zip 文件,但它的大小为 0 字节。

后端脚本 (PHP) 中的相关部分是:

header("X-Sendfile: $full_path");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');

PHP脚本确实可以在文件系统中找到文件。但是,我不确定是服务器还是应用程序出了问题。我的猜测是它与应用程序有关。我的第一个假设是它与缓存有关,但为什么它只影响这个文件而不影响任何其他文件?

为什么我会遇到这个问题,我该如何解决?

您可以尝试将 content-type 修改为:

fetch("https://example.com/apptools/getZipPack.php", {
      method: "POST",
      headers:{  
        'Content-Type': 'multipart/form-data',
      },
    })

这似乎是正在发生的事情:

  1. 使用 X-Sendfile 会触发生成 ETag,即使 POST 响应通常不会被缓存。

  2. React Native 客户端缓存响应,可能是因为 ETag 的存在。当它在缓存条目过时后再次访问该资源时,它会发送 conditional request。因为这是一个 POST,它使用 If-Match header——用于资源的条件更新——这根本不是你想要做的。

  3. 服务器看到 If-Match header 并尝试进行条件更新。也就是说,如果旧的 ETag 与当前的匹配,它只会执行 POST。如果不是这种情况,它 returns 一个 412 Precondition Failed 响应。

既然你说你不想在这里使用缓存,最简单的解决方案可能是在你的响应中添加 Cache-Control: no-store。这可能会阻止 Apache 完全生成 ETag,但无论如何它肯定会阻止客户端存储响应,确保它无法发送任何条件请求。