为什么AWS S3存储桶显示无效的参数类型?

时间:2019-09-28 08:15:55

标签: reactjs amazon-s3 django-rest-framework

我正在尝试将图像上传到s3存储桶,但显示以下错误。我正在使用React和DRF。

我正在关注this tutorial。在那里,他们建议先从后端生成签名的URL,然后在签名的URL的帮助下从响应发出请求到s3服务器以上传图像。

Traceback:

File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  54.         return view_func(*args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  497.             response = self.handle_exception(exc)

File "/app/.heroku/python/lib/python3.7/site-packages/rest_framework/views.py" in handle_exception
  457.             self.raise_uncaught_exception(exc)

File "/app/.heroku/python/lib/python3.7/site-packages/rest_framework/views.py" in raise_uncaught_exception
  468.         raise exc

File "/app/.heroku/python/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  494.             response = handler(request, *args, **kwargs)

File "/app/articles/api/views.py" in get
  82.             ExpiresIn=3600

File "/app/.heroku/python/lib/python3.7/site-packages/botocore/signers.py" in generate_presigned_post
  688.         {'Bucket': bucket}, operation_model)

File "/app/.heroku/python/lib/python3.7/site-packages/botocore/validate.py" in serialize_to_request
  297.                 raise ParamValidationError(report=report.generate_report())

Exception Type: ParamValidationError at /api/blog/api/create-permission/aws-s3/
Exception Value: Parameter validation failed:
Invalid type for parameter Bucket, value: None, type: <class 'NoneType'>, valid types: <class 'str'>
Request information:
USER: XXXder

GET:
image_name = 'asd_adasd_thumb.jpg'

POST: No POST data

FILES: No FILES data

settings.py

AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_S3_REGION_NAME = 'ap-south-1'
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME

AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}

我如何生成signed_url

import os
import datetime
import boto3
import mimetypes


s3 = boto3.client('s3', config=Config(signature_version='s3v4'))


class SignS3Upload(APIView):
    # authentication_classes = (authentication.SessionAuthentication,)
    # permission_classes = [IsAuthenticated, ]

    def get(self, request):
        s3_bucket = os.environ.get('AWS_STORAGE_BUCKET_NAME')
        file_name = request.GET['image_name']
        file_type = mimetypes.guess_type(file_name)[0]
        presigned_post = s3.generate_presigned_post(

            Bucket=s3_bucket,
            Key=file_name,
            Fields={"acl": "public-read", "Content-Type": file_type},
            Conditions=[
                {"acl": "public-read"},
                {"Content-Type": file_type},
                {"x-amz-algorithm": "AWS4-HMAC-SHA256"}

            ],
            ExpiresIn=3600
        )

        data = {
            "signed_url": presigned_post,
            'url': 'https://%s.s3.amazonaws.com/%s' % (s3_bucket, file_name)
        }
        return Response(data)

在前端,我将React与Redux结合使用。

export const getSignedRequest = (image) => (dispatch, getState) => {


    const image_name = image.name
    const type = image.type

    axios.get('https://abc-abc.herokuapp.com/api/blog/api/create-permission/aws-s3/', { params: { image_name } })
        .then((res) => {
            dispatch({
                type: GET_PARTICULAR_BLOG_IMG_UPLOAD,
                payload: res.data
            });

            var postData = new FormData();
            var key
            for (key in res.data.signed_url.fields) {

                postData.append(key, res.data.signed_url.fields[key]);
            }
            postData.append('file', image);

            ////////// Here I'm sending request to s3//////////////////////

            return axios.post(res.data.signed_url.url, postData);
        })
        .then((res) => {
            dispatch({
                type: GET_PARTICULAR_BLOG_IMG_UPLOAD_AWS,
                payload: res.data
            });
        })
        .catch(err => {
            //dispatch(returnErrors(err.response.data, err.response.status));
            console.log(err)
            dispatch({
                type: GET_PARTICULAR_BLOG_IMG_UPLOAD_FAIL
            });
        });

};

这是表格

    fileSelectHandler = event => {

        const files = document.getElementById("file_input").files;
        const file = files[0]

        if (!file) {
            return alert("Please select Image")
        }

        this.props.getSignedRequest(file)

    }


 <Form onSubmit={this.handleSubmit} className='blog_form' encType="multipart/form-data" method='POST'>

<input className="upload" type="file" accept='image/*' name='image' onChange={this.fileSelectHandler} id="file_input" />
<Button type="primary" htmlType='submit' className="BlogButton">Create Blog</Button>
</Form>

已编辑:

当我从本地主机尝试时,它似乎正在生成签名的URL Please check this screenshot

但是响应状态(来自s3)为204 Please check this images

从本地主机正常运行,将图像上传到s3。但是当我将其部署到heroku时却没有。从heroku尝试时,出现了上面提到的相同错误。

1 个答案:

答案 0 :(得分:0)

如错误所示,传递给参数Bucket的值为'None',这是无效值。

Exception Value: Parameter validation failed:
Invalid type for parameter Bucket, value: None, type: <class 'NoneType'>, valid types: <class 'str'>

您正在从系统环境中读取存储桶名称

s3_bucket = os.environ.get('AWS_STORAGE_BUCKET_NAME')

在heroku环境中可能未设置。

如果环境中不存在密钥,则os.environ.get函数将返回None。