我创建了签名 URL,用于从客户端直接将文件(最大 1GB 的 mp3 视频文件)上传到云存储上。但是当我尝试上传文件时,出现以下错误:
<Code> SignatureDoesNotMatch </Code> <Message> Access denied. </Message> <Details> The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. </Details>
这就是 URL 的生成方式:
https://storage.googleapis.com/bucket-name/filename?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=something.iam.gserviceaccount.com%2xyz%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20240207T120631Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=21...
前端处于反应状态,我首先调用以获取signedurl,然后上传文件。 反应代码:
const responseForSignedUrl = await axios.get(
`${baseUrl}/api/posts/getuploadurl/`
);
if (responseForSignedUrl.status !== 200) {
throw new Error("Failed to obtain signed URL.");
}
const signedUrl = responseForSignedUrl.data.url;
// Upload video file to Cloud Storage using the signed URL
const videoFormData = new FormData();
videoFormData.append("file", video_file);
const uploadResponse = await axios.put(signedUrl, videoFormData, {
headers: {
"Content-Type": "video/mp4",
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
setProgress(percentCompleted);
},
});
if (!uploadResponse.ok) {
throw new Error("Failed to upload video file.");
}
生成签名 URL 的后端代码:
def generate_upload_signed_url_v4(request):
"""Generates a v4 signed URL for uploading a blob using HTTP PUT.
Note that this method requires a service account key file. You can not use
this if you are using Application Default Credentials from Google Compute
Engine or from the Google Cloud SDK.
"""
bucket_name = 'production-bucket-name'
blob_name = 'test1'
# storage_client = storage.Client()
current_directory = os.path.dirname(os.path.abspath(__file__))
# Navigate to the parent directory (folder A)
parent_directory = os.path.dirname(current_directory)
# Access file B within folder A
file_b_path = os.path.join(parent_directory, "service-credentials.json")
storage_client = storage.Client.from_service_account_json(file_b_path)
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(blob_name)
print(storage_client)
url = blob.generate_signed_url(
version="v4",
# This URL is valid for 15 minutes
expiration=datetime.timedelta(minutes=15),
# Allow PUT requests using this URL.
method="PUT",
content_type="video/mp4",
)
return JsonResponse({'url': url})
我发现了这个错误。使用 FormData() 形成请求将始终发送内容类型为“multipart/form-data”的请求。因此,即使我们使用任何其他内容类型创建签名 url,它也会失败。我不知道为什么当我在签名网址时使用表单数据时它也失败了。无论如何,我们只需要发送文件,只是视频文件,同时发送请求而不是表格。