从ReactNative(iOS)直接发布到AWS S3,带有签名请求

问题描述:

我试图重新创建在React Native中上传用户提交图片的Heroku example,但不断收到来自AWS的400个错误。从ReactNative(iOS)直接发布到AWS S3,带有签名请求

图像来自iOS相机胶卷。我有这个图像的uri和一个base64编码的图像版本。 MIME类型是image/jpeg。到目前为止,我已经按照Heroku的说法设置了所有的东西,但是我无法让文件发送正确的形状。我在下面添加了我的代码以进行说明。

我使用react-native-image-picker选择来自相机胶卷图像

客户端代码

module.exports = React.createClass({ 
... 

openPhotos() { // called on a button press, opens camera roll 
    ImagePicker.showImagePicker(options, (response) => { 
     if (response.didCancel) return; 
     if (response.error) return Alert.alert('ImagePicker Error: ', response.error); 
     this.getSignedRequest(response); 
    }); 
    }, 
getSignedRequest(pickerResp) { 
    // image uri IS pickerResp.uri AS IS `data:image/jpg;base64,${pickerResp.data}` 
    var file = { 
    uri: pickerResp.uri, // ALSO WORKS `data:image/jpg;base64,${pickerResp.data}`, 
    name: `${this.props.email}.jpg`, 
    type: 'image/jpeg' 
    }; 
    var body = new FormData(); 
    body.append('file', file); 

    fetch(`${api.TEST_API}sign-s3?file-name=${file.name}.jpg&file-type=${file.type}`, { 
    method: "GET" 
    }).then(response => response.json()).then(data => { 
    Alert.alert("GOT SIGNED REQUEST",data.url); 
    fetch(data.signedRequest, { method: 'put', body: body }).then(resp => resp.json()).then(data => { 
     this.setState({uploadedFile: true}); 
     this.props.toSignup(); 
    }).catch(err => Alert.alert("Server Error", "Could not upload profile photo.")) 
    }).catch(err => { 
    Alert.alert('S3 Signed Request Error',"Look at the Logs"); 
    console.log(err); 
    }) 
}, 

... 
}; 

服务器端代码

AWS NPM包:AWS-SDK]

// get signature to upload client side to s3 
apiRouter.get('/sign-s3', function(req, res) { 
    console.log('signing s3') 
    const s3 = new aws.S3(); // automatically loads key and secret 
    const fileName = req.query['file-name']; 
    const fileType = req.query['file-type']; 
    const s3Params = { 
    Bucket: S3_BUCKET, 
    Key: fileName, 
    Expires: 60, 
    ContentType: fileType, 
    ACL: 'public-read' 
    }; 

    s3.getSignedUrl('putObject', s3Params, (err, data) => { 
    if(err) return console.log(err) 
    const returnData = { 
     signedRequest: data, 
     url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}` 
    }; 
    console.log(returnData) 
    res.json(returnData); 
    }); 
}); 

fetch(`${api.TEST_API}sign-s3?file-name=${file.name}.jpg&file-type=${file.type}

此处的获取请求将在所描述的文件名的末尾添加一个额外的.jpg,因此当您尝试上传到S3时,它将被拒绝,因为有效负载与先前请求不匹配。另外,Heroku建议的XMLHttpRequest可以很好地处理文件结构,但是使用base64数据(下文)。

var file = { 
    uri: `data:image/jpg;base64,${pickerResp.data}`, //pickerResp.uri, 
    type: 'image/jpeg', 
    name: `${this.props.email}.jpg`, 
}; 

以下XML HTTP请求将在其编码形式发送图像到S3,在那里一个简单的GET请求或图像的src属性可以访问该文件。

function uploadFile(file, signedRequest, url, cb) { 
    const xhr = new XMLHttpRequest(); 
    xhr.open('PUT', signedRequest); 
    xhr.onreadystatechange = function() { 
    if (xhr.readyState === 4) { 
     if(xhr.status === 200) { 
     cb() 
     } else { 
     alert('Could not upload file.'); 
     } 
    } 
    }; 
    xhr.send(file); 
};