Как загрузить файл на S3, используя предопределенный URL-адрес с React.js - PullRequest
0 голосов
/ 07 января 2019

Я генерирую предварительно назначенный URL-адрес S3 для загрузки файла из локальной сети. На фронтенде я использую React.

Я получаю предварительно назначенный URL с помощью вызова API, а затем пытаюсь загрузить файл с помощью axios, но он выдает 403 (Запрещено).

Если я использую тот же предопределенный URL, используя 'curl', тогда он работает нормально, и тот же файл загружается на S3.

s3.py - Для создания предварительно подписанного URL:

class S3Controller:
    def __init__(self, client=None, bucket=None):
        self.client = client
        self.bucket = bucket

    def signed_url(self, filename):
        filename = filename.replace('/', '-').replace(' ', '-')
        date = datetime.now()
        key = f"audio/{date.year}/{date.month}/{date.day}/{filename}"
        url = self.client.generate_presigned_url(
            ClientMethod='put_object',
            ExpiresIn=3600,
            Params={
                'Bucket': self.bucket,
                'Key': key,
            }
        )
        return url

Компонент в реакции на загрузку файла:

import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { S3SignedUrl } from '../query';
import { withApollo } from 'react-apollo';
import AudioUploadButton from '../components/AudioUploadButton';
import axios from 'axios';


class UpdateAudio extends Component {
    constructor(props) {
        super(props);
        this.site = "5d517862-0630-431c-94b1-bf34de6bfd8b"
        this.state = {
            audioSelected: {},
            audioLoaded: 0
        }
        this.onSelect = this.onSelect.bind(this);
        this.onUpload = this.onUpload.bind(this);
    }

    onSelect = (event) => {
        const fileInfo = event.target.files[0];
        this.setState({audioSelected: fileInfo});
    }

    onUpload = async () => {
        let resp = await this.props.client.query({ query: S3SignedUrl, variables: {filename: this.state.audioSelected.name}});

        let { data } = resp;
        let endpoint = data.s3SignedUrl.url;

        axios.put(endpoint, this.state.audioSelected, {
            onUploadProgress: ProgressEvent => {
                this.setState({
                    audioLoaded: (ProgressEvent.loaded / ProgressEvent.total*100)
                })
            }
        })
        .then(res => {
            console.log(res);
        })

    }

    render() {
        return (
            <Fragment>
                <AudioUploadButton onSelect={this.onSelect} onUpload={this.onUpload} audioSelected={this.state.audioSelected} audioLoaded={this.state.audioLoaded} />
            </Fragment>
        )
    }
}

UpdateAudio = withRouter(UpdateAudio)

export default withApollo(UpdateAudio);

AudioUploadButton.js

import React from 'react';
import { Grid, Button, Typography, Fab } from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';
import CloudUploadIcon  from '@material-ui/icons/CloudUpload';


const styles = theme => ({
button: {
    margin: theme.spacing.unit,
},
input: {
    display: 'none',
},
fab: {
    margin: theme.spacing.unit,
},
});


class AudioUploadButton extends React.Component {
    render() {
        let { classes } = this.props;
        let { name, size } = this.props.audioSelected;
        let loaded = this.props.audioLoaded;

        return (
            <Grid container spacing={8} >
                <Grid item md={2} xs={12}>
                    <input
                        accept="audio/*"
                        className={classes.input}
                        id="contained-button-file"
                        type="file"
                        onChange = {this.props.onSelect}
                    />
                    <label htmlFor="contained-button-file">
                        <Button variant="contained" component="span" className={classes.button}>Select</Button>
                    </label>
                </Grid>
                <Grid item md={1} xs={12}>
                    <Fab color="secondary" size='medium' onClick={this.props.onUpload}>
                        <CloudUploadIcon />
                    </Fab>
                </Grid>

                <Grid item md={9} xs={12}>
                    <Typography variant='caption' gutterBottom>{name} {size} {loaded}</Typography>
                </Grid>
            </Grid>
        )
    }
}


export default withStyles(styles)(AudioUploadButton);

Curl работает без проблем:
curl -X PUT --upload-file 1.jpg https://s3.amazonaws.com/bucket-name/filepath.jpg?AWSAccessKeyId=xyz&Signature=Vql3Bnkb7H847Cr4vtw5gbi%2F%2Bs%3D&Expires=1546873244

Спасибо за помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...