Reputation: 165
I'm trying to upload an image to google drive. I followed this tutorial for uploading files, I can upload a simple file but when I try to upload an image I get a corrupted image. In fact, the file is uploaded but the image I want to see is corrupted. I know that the problem is in the body of the request but I don't know where because it's a strange way (boundaries, delimiters, etc). Everything works fine except for the content of the image so you just need to look at the post method that uploads the image. This is my code:
import { Upload } from 'antd';
import React, { Component } from 'react';
var SCOPE = 'https://www.googleapis.com/auth/drive.file';
var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';
class App extends Component {
state = {
name: '',
googleAuth: '',
body: ''
}
componentDidMount(){
var script = document.createElement('script');
script.onload=this.handleClientLoad;
script.src="https://apis.google.com/js/api.js";
document.body.appendChild(script);
}
initClient = () => {
try{
window.gapi.client.init({
'apiKey': "apikey",
'clientId': "clientid",
'scope': SCOPE,
'discoveryDocs': [discoveryUrl]
}).then(() => {
this.setState({
googleAuth: window.gapi.auth2.getAuthInstance()
})
this.state.googleAuth.isSignedIn.listen(this.updateSigninStatus);
document.getElementById('signin-btn').addEventListener('click', this.signInFunction);
document.getElementById('signout-btn').addEventListener('click', this.signOutFunction);
});
}catch(e){
console.log(e);
}
}
signInFunction =()=>{
console.log(this.state.googleAuth)
this.state.googleAuth.signIn();
console.log(this.state.googleAuth)
this.updateSigninStatus()
}
signOutFunction =()=>{
this.state.googleAuth.signOut();
this.updateSigninStatus()
}
updateSigninStatus = ()=> {
this.setSigninStatus();
}
setSigninStatus= async ()=>{
console.log(this.state.googleAuth.currentUser.get())
var user = this.state.googleAuth.currentUser.get();
console.log(user)
if (user.wc == null){
this.setState({
name: ''
});
}
else{
var isAuthorized = user.hasGrantedScopes(SCOPE);
if(isAuthorized){
console.log('USER')
console.log(user)
this.setState({
name: user.vt.Ad
});
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileName='mychat123.png';
var contentType='image/png'
var metadata = {
'name': fileName,
'mimeType': contentType
};
var multipartRequestBody =
delimiter + 'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n';
multipartRequestBody += + '\r\n' + this.state.body;
multipartRequestBody += close_delim;
console.log(multipartRequestBody);
var request = window.gapi.client.request({
'path': 'https://www.googleapis.com/upload/drive/v3/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {
'Content-Type': contentType
},
'body': multipartRequestBody
});
request.execute(function(file) {
console.log(file)
});
}
}
}
getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
handleChange = async file => {
let image
if (file.currentTarget) {
image = file.currentTarget.currentSrc;
console.log(file.currentTarget)
} else {
if (!file.file.url && !file.file.preview) {
file.file.preview = await this.getBase64(file.file.originFileObj);
}
image = file.file.preview;
console.log(file.file)
this.setState({
body: file.file.preview
});
}
console.log(image)
}
handleClientLoad = ()=>{
window.gapi.load('client:auth2', this.initClient);
}
render() {
return (
<div className="App">
<Upload
style={{width: '100%', height: '200px' }}
listType="picture-card"
onChange={this.handleChange} >
<div>
<div style={{ marginTop: 8 }}>Subir imagen</div>
</div>
</Upload>
<div>UserName: <strong>{ this.state.name}</strong></div>
<button id="signin-btn">Sign In</button>
<button id="signout-btn">Sign Out</button>
</div>
);
}
}
export default App;
Why is my file corrupted? It says that this is not a PNG file. What's the correct way to upload an image to drive? Thank you!!
Upvotes: 3
Views: 3943
Reputation: 1041
I created a customization on top of react-uploady, called drive-uploady that makes it trivial to upload to google drive with all the power of Uploady: hooks, UI components, progress, etc.
for example:
import React from "react";
import DriveUploady from "drive-uploady";
import UploadButton from "@rpldy/upload-button";
export const App = () => {
return <DriveUploady
clientId="<my-client-id>"
scope="https://www.googleapis.com/auth/drive.file"
>
<UploadButton>Upload to Drive</UploadButton>
</DriveUploady>;
};
Is all you need to render an upload button that logs in the user to Drive and uploads.
Upvotes: 0
Reputation: 201428
this.state.body
is the base64 data. In this case, it is required to add Content-Transfer-Encoding: base64
to the header of data in the request body.
multipart/form-data
.uploadType=multipart
, please set multipart/form-data; boundary=###
to the header as the content type. In your script, it seems that the content type is image/png
.When above points are reflected to your script, it becomes as follows.
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileName='mychat123.png';
var contentType='image/png'
var metadata = {
'name': fileName,
'mimeType': contentType
};
var multipartRequestBody =
delimiter + 'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n';
multipartRequestBody += + '\r\n' + this.state.body;
multipartRequestBody += close_delim;
console.log(multipartRequestBody);
var request = window.gapi.client.request({
'path': 'https://www.googleapis.com/upload/drive/v3/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {
'Content-Type': contentType
},
'body': multipartRequestBody
});
To:
const boundary = '-------314159265358979323846';
const delimiter = "--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileName = 'mychat123.png';
var contentType = 'image/png';
var metadata = {'name': fileName,'mimeType': contentType};
var multipartRequestBody = delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) + "\r\n" +
delimiter +
'Content-Type: ' + contentType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n\r\n' +
this.state.body +
close_delim;
console.log(multipartRequestBody);
var request = window.gapi.client.request({
'path': 'https://www.googleapis.com/upload/drive/v3/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {'Content-Type': 'multipart/form-data; boundary=' + boundary},
'body': multipartRequestBody
});
multipart/form-data
and multipart/related
can be used.From your replying, I noticed that you wanted to use fetch
instead of gapi.client.request
. In this case, the sample script is as follows.
This is your script in your comment.
const fd = new FormData();
fd.append("file", this.state.body);
fd.append("title", 'test.png');
const options = {
method: 'POST',
headers: { Authorization: "Bearer" + " " + window.gapi.auth.getToken().access_token },
body: fd
};
await fetch("googleapis.com/upload/drive/v3/files", options)
.then(response => response.json())
.then(jsonResp => { console.log(jsonResp) });
name
.uploadType=multipart
is required to be used in the endpoint.When above points are reflected to your script, it becomes as follows.
// Reference: https://stackoverflow.com/a/16245768
// This method converts from base64 data to blob.
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
const metadata = {name: 'test.png'};
const fd = new FormData();
fd.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
fd.append('file', b64toBlob(this.state.body, "image/png"));
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
method: 'POST',
headers: {Authorization: 'Bearer ' + window.gapi.auth.getToken().access_token},
body: fd
})
.then(response => response.json())
.then(jsonResp => { console.log(jsonResp) });
Upvotes: 4