为什么我在后续下载特定文件时会在 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 文件,我遇到了以下问题:
- 我下载安装zip包
- 我删除压缩包
- 我尝试再次下载,但这次出现 412 错误。
无论我尝试重新下载多少次,它都不会让我。为了克服这个问题,我执行以下操作:
- 用其他 zip 文件之一替换服务器上的 zip 文件
- 现在可以下载了
- 我从应用程序中删除包
- 我把服务器上的zip文件替换成原来的
- 我现在可以下载压缩包了
这一次有效,但如果我删除它并尝试再次安装,那么它又不会让我。
我也尝试过另一种方法,在这种情况下,我用有问题的 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',
},
})
这似乎是正在发生的事情:
使用 X-Sendfile
会触发生成 ETag
,即使 POST
响应通常不会被缓存。
React Native 客户端缓存响应,可能是因为 ETag
的存在。当它在缓存条目过时后再次访问该资源时,它会发送 conditional request。因为这是一个 POST
,它使用 If-Match
header——用于资源的条件更新——这根本不是你想要做的。
服务器看到 If-Match
header 并尝试进行条件更新。也就是说,如果旧的 ETag
与当前的匹配,它只会执行 POST
。如果不是这种情况,它 returns 一个 412 Precondition Failed
响应。
既然你说你不想在这里使用缓存,最简单的解决方案可能是在你的响应中添加 Cache-Control: no-store
。这可能会阻止 Apache 完全生成 ETag
,但无论如何它肯定会阻止客户端存储响应,确保它无法发送任何条件请求。
我创建了一个 React Native 应用程序,它允许我将 Zip 包下载到我的应用程序中。在大多数情况下它都有效,我可以下载 zip 包,删除 zip 包,然后重新下载。但是,对于我的一个 zip 文件,我遇到了以下问题:
- 我下载安装zip包
- 我删除压缩包
- 我尝试再次下载,但这次出现 412 错误。
无论我尝试重新下载多少次,它都不会让我。为了克服这个问题,我执行以下操作:
- 用其他 zip 文件之一替换服务器上的 zip 文件
- 现在可以下载了
- 我从应用程序中删除包
- 我把服务器上的zip文件替换成原来的
- 我现在可以下载压缩包了
这一次有效,但如果我删除它并尝试再次安装,那么它又不会让我。
我也尝试过另一种方法,在这种情况下,我用有问题的 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',
},
})
这似乎是正在发生的事情:
使用
X-Sendfile
会触发生成ETag
,即使POST
响应通常不会被缓存。React Native 客户端缓存响应,可能是因为
ETag
的存在。当它在缓存条目过时后再次访问该资源时,它会发送 conditional request。因为这是一个POST
,它使用If-Match
header——用于资源的条件更新——这根本不是你想要做的。服务器看到
If-Match
header 并尝试进行条件更新。也就是说,如果旧的ETag
与当前的匹配,它只会执行POST
。如果不是这种情况,它 returns 一个412 Precondition Failed
响应。
既然你说你不想在这里使用缓存,最简单的解决方案可能是在你的响应中添加 Cache-Control: no-store
。这可能会阻止 Apache 完全生成 ETag
,但无论如何它肯定会阻止客户端存储响应,确保它无法发送任何条件请求。