使用 django-storages 在 google 云存储中上传文件时出现 404

404 on file upload in google cloud storage with django-storages

当使用 django-storages 库将文件上传到 google 云时,我得到

NotFound at /accounts/profile/1/edit/
404 POST https://storage.googleapis.com/upload/storage/v1/b/moneygame-42/o?uploadType=multipart: ('Request failed with status code', 404, 'Expected one of', <HTTPStatus.OK: 200>)

这是我在 settings.py 中的代码:

DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
GS_PROJECT_ID = GS_BUCKET_NAME = 'moneygame-42'

GS_CREDENTIALS = service_account.Credentials.from_service_account_file(
    "moneygame-42-1fa2467e74eb.json"
)

这里是错误:

Internal Server Error: /accounts/profile/1/edit/
Traceback (most recent call last):
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/cloud/storage/blob.py", line 1795, in upload_from_file
    if_metageneration_not_match,
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/cloud/storage/blob.py", line 1654, in _do_upload
    if_metageneration_not_match,
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/cloud/storage/blob.py", line 1321, in _do_multipart_upload
    response = upload.transmit(transport, data, object_metadata, content_type)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/resumable_media/requests/upload.py", line 108, in transmit
    self._process_response(response)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/resumable_media/_upload.py", line 109, in _process_response
    _helpers.require_status_code(response, (http_client.OK,), self._get_status_code)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/resumable_media/_helpers.py", line 96, in require_status_code
    *status_codes
google.resumable_media.common.InvalidResponse: ('Request failed with status code', 404, 'Expected one of', <HTTPStatus.OK: 200>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/views/generic/base.py", line 97, in dispatch
    return handler(request, *args, **kwargs)
  File "/Users/jayjayjay/programming/project/moneygame/user/views.py", line 120, in post
    form_p.save()
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/forms/models.py", line 458, in save
    self.instance.save()
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/base.py", line 741, in save
    force_update=force_update, update_fields=update_fields)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/base.py", line 779, in save_base
    force_update, using, update_fields,
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/base.py", line 848, in _save_table
    for f in non_pks]
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/base.py", line 848, in <listcomp>
    for f in non_pks]
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/fields/files.py", line 288, in pre_save
    file.save(file.name, file.file, save=False)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/db/models/fields/files.py", line 87, in save
    self.name = self.storage.save(name, content, max_length=self.field.max_length)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/django/core/files/storage.py", line 52, in save
    return self._save(name, content)
  File "/Users/jayjayjay/programming/project/moneygame/src/django-storages/storages/backends/gcloud.py", line 160, in _save
    content_type=file.mime_type, predefined_acl=self.default_acl)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/cloud/storage/blob.py", line 1799, in upload_from_file
    _raise_from_invalid_response(exc)
  File "/Users/jayjayjay/.conda/envs/moneygame/lib/python3.7/site-packages/google/cloud/storage/blob.py", line 3091, in _raise_from_invalid_response
    raise exceptions.from_http_status(response.status_code, message, response=response)
google.api_core.exceptions.NotFound: 404 POST https://storage.googleapis.com/upload/storage/v1/b/moneygame-42/o?uploadType=multipart: ('Request failed with status code', 404, 'Expected one of', <HTTPStatus.OK: 200>)
[25/Jun/2020 15:53:17] "POST /accounts/profile/1/edit/ HTTP/1.1" 500 180335

经过一番试验,我发现桶名应该包含.appspot.com部分。这是我的新设置:

DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
GS_PROJECT_ID = 'moneygame-42'
GS_BUCKET_NAME = 'moneygame-42.appspot.com'

GS_CREDENTIALS = service_account.Credentials.from_service_account_file(
    "moneygame-42-1fa2467e74eb.json"
)