Upload file to Google Drive with React and Google App script - javascript

I am trying to upload file with Google App script and React.
Google Script:
function uploadArquivoParaDrive(base64Data, nomeArq, idPasta) {
try{
var splitBase = base64Data.split(','),
type = splitBase[0].split(';')[0].replace('data:','');
var byteCharacters = Utilities.base64Decode(splitBase[1]);
var ss = Utilities.newBlob(byteCharacters, type);
ss.setName(nomeArq);
var file = DriveApp.getFolderById(idPasta).createFile(ss);
return file.getName();
}catch(e){
return 'Erro: ' + e.toString();
}
}
I can run this ant it works:
function uploadFile() {
var image = UrlFetchApp.fetch('url to some image').getBlob();
var file = {
title: 'google_logo.png',
mimeType: 'image/png'
};
file = Drive.Files.insert(file, image);
Logger.log('ID: %s, File size (bytes): %s', file.id, file.fileSize);
}
This is React script:
onSubmit = (e) => {
e.preventDefault();
axios.get(url, {...this.state}, {
headers: {
'Content-Type': 'multipart/form-data'
}
}, (response) => {
console.log(response);
})
};
setFile = (event) => {
console.log(event.target.files)
this.setState({file: event.target.files[0]});
};
render() {
return (
<form>
<input type="file" id="file" onChange={this.setFile} />
<button onClick={this.onSubmit}>ADD</button>
</form>
)
}
I am trying with POST, but I am getting 400 response. I know that this can't be GET request, but and with it, I am getting - 200 with no response.
I can insert rows in sheets, but I want to upload files to Google Drive with Google App Scripts.
I know that there is a way to upload files via Google Scripts and React, because, there is a way without React (google.script.run).

Here are two different approaches that are used in mixed mode. It's unacceptable in some contexts.
Let's say softly 'React is a dummy'. This is an add-on that you should always avoid when somewhat comes to something that you depend on, but you cannot change. See what SOLID is.
Below it is always assumed that you are working in a browser. Your web-pages are hosted in the web application of the Google Apps Script.
The first approach. Using XMLHttpRequests
On client side you have to use XMLHttpRequests call from your browser.
On server side you have to use doGet doPost reserved functions. Always transfer data in a clear and simple format. This will save time searching for errors.
Example https://stackoverflow.com/a/11300412/1393023
The second approach. Using Client-side API
On client side you have to use google.script.run call from your browser.
On server side you have to use your functions. Always transfer data in a clear and simple format. This will save time searching for errors.
Example https://stackoverflow.com/a/15790713/1393023
Consequence
Your example has signs of mixing approaches. Unfortunately, it cannot be quickly debugged.
There is no reason that React is causing the problem. If so, then your architecture is incorrect.
If you want to use axios, then you need to consider the first approach.
If you want to use google.script.run then you need to catch onSubmit then you need to call an interface that implement google.script.run. Usually asynchronously, since the last call will still be completed with a callback.

Related

WordPress - ERR_BAD_RESPONSE admin-ajax-php file upload

I'm trying to upload files in WordPress using admin-ajax.php
I have this code in my functions.php file
function upload_docs(){
var_dump($_FILES);
}
add_action('wp_ajax_nopriv_upload_docs', 'upload_docs');
add_action('wp_ajax_upload_docs', 'upload_docs');
The function at the moment is a test that I want to use to debug what information is passed from the front-end which is a Vue app hosted in a page template.
I've correctly loaded and localized the Vue CSS and js files and after the build, in my localhost, I'm able to pass the other forms I have configured on my functions file
On the front-end side, the Vue app has this method that will add the needed information to the WordPress backend
sendUploadForm(){
let documents = this.$refs.uploadedFiles.files
let userData = new FormData()
for(let i = 0; i < documents.length; i++ ){
userData.append('file[]', documents[i])
}
userData.append('action', 'upload_docs')
axios.post(wp_param.ajaxurl, userData).then( res => {
console.log(res)
}).catch( e => console.log(e) )
}
What is going wrong with the code? I will always get a 500 error status code ERR_BAD_RESPONSE and I don't know if the function is called because I'm unable to see any var_dump or var_export from PHP. I've tried to enable the debug in wp-config file but nothing changed.
Any suggestion?
Add these additional two lines under your WP_DEBUG one, make the error happen again, and check in the folder wp-content/ if you see a new file debug.log.
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Consider:
Implementing nonces and permission checks to protect your endpoints
If only logged in users are supposed to be able to upload files, ensure the no_priv method is properly locked down
Using a custom REST API endpoint instead of admin-ajax https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
Terminate your function with wp_die(); when using AJAX in WordPress
I was stuck with this problem because of WordPress plugin conflicts.
You can disable unused plugins and check.

How to read a file being saved to Parse server with Cloud Code, before actually saving it?

I'm trying to use Cloud Code to check whether a user-submitted image is in a supported file type and not too big.
I know I need to do this verification server-side and I think I should do it with Cloud Code using beforeSave – the doc even has a specific example about data validation, but it doesn't explain how to handle files and I couldn't figure it out.
I've tried the documented method for saving files, ie.
file = fileUploadControl.files[0];
var parseFile = new Parse.File(name, file);
currentUser.set("picture", parseFile);
currentUser.save();
and in the Cloud Code,
Parse.Cloud.beforeSave(Parse.User, (request, response) => { // code here });
But 1. this still actually saves the file on my server, right? I want to check the file size first to avoid saving too many big files...
And 2. Even then, I don't know what to do in the beforeSave callback. It seems I can only access the URL of the saved image (proof that it has been uploaded), and it seems very counter-intuitive to have to do another https request to check the file size and type before deciding whether to proceed with attaching the file to the User object.
(I'm currently using remote-file-size and file-type to check the size and type of the uploaded file, but no success here either).
I also tried calling a Cloud function, but it feels like I'm not doing the right thing, and besides I'm running into the same issues.
I can call a Cloud function and pass a saved ParseFile as a parameter, and then I know how to save it to the User object from the Cloud Code using the masterkey, but as above it still involves uploading the file to the server and then re-fetching it using its URL.
Am I missing anything here?
Is there no way to do something like a beforeSave on Parse.File, and then stop the file from being saved if it doesn't meet certain criteria?
Cheers.
If you have to do something with files, parse lets you overwrite the file adapter to handle file operations.
You can indicate the file adapter to use in your ParseServer instatiation:
var FSStoreAdapter = require('./file_adapter');
var api = new ParseServer({
databaseURI: databaseUri ,
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
appId: process.env.APP_ID,
filesAdapter: fs_store_adapter, // YOUR FILE ADAPTER
masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret!
serverURL: "https://yourUrl", // Don't forget to change to https if needed
publicServerURL: "https://yourUrl",
liveQuery: {
classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
}
maxUploadSize: "500mb" //you will now have 500mb limit :)
});
That said, you can also specify a maxUploadSize in your instatiation as you can see in the last line.
you have to use save in background
file = ParseFile("filename", file)
file?.saveInBackground({ e ->
if (e == null) {
} else {
Toast.makeText(applicationContext, "Error: $e", Toast.LENGTH_SHORT).show()
e.printStackTrace()
Log.d("DEBUG", "file " + e.code)
}
}, { percentDone ->
Log.d("DEBUG", "file:" + percentDone!!)
})

How do I return a file to the user?

I am trying to return a file to the user.
"GetExcel" appears to work and in debug I can see that "ba" has data.
The method completes BUT nothing appears to be returned to the browser - I am hoping to see the file download dialog.
C#
public FileResult GetExcel()
{
using (ExcelPackage pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Demo");
ws.Cells["A1"].Value = "LBHERE";
var ba = pck.GetAsByteArray();
return File(ba, "text/plain", "testFile.txt");
}
}
Javascript
function clickedTest() {
alert("Test clicked");
$.get(myPath + "/Employee/GetExcel", { }, function (data) {
})
};
jQuery's $.get() function pulls data from a webpage into your client-side scripts through AJAX. This is not what you want to do if you want to download a file. Instead, you should open a new tab set to the URL of the file you wish to download.
Try this:
function clickedTest() {
window.open(myPath + "/Employee/GetExcel", "_blank");
}
If your browser still isn't initiating a download, but is instead just displaying a file, you may have to go one step further.
Somewhere in your server-side code, when you have access to the Response object, you should set the Content-Disposition header to attachment and provide a filename for the spreadsheet you are serving. This will inform your browser that the file you are requesting is meant to be downloaded, not displayed.
This can be done as follows:
Response.AddHeader("Content-Disposition", "attachment;filename=myfile.xls")
Of course, replace myfile.xls with the proper filename for your spreadsheet.

Why doesn't Microsoft Skydrive download multiple files even though MS example shows it? (wl.download)

Summary
I am attempting to find out why the wl.download function will not download more than one file even though the Microsoft examples seem to indicate that they can.
And, the code seems to be called for each file you attempt to download, but only the one file is actually downloaded.
Details
Here are the details of how you can see this problem which I've tried in IE 11.x and Chrome 30.x
If you will kindly go to :
http://isdk.dev.live.com/dev/isdk/ISDK.aspx?category=scenarioGroup_skyDrive&index=0
You will be able to run an example app which allows you to download files from your skydrive.
Note: the app does require you to allow the app to access your skydrive.
Once you get there you'll see code that looks like this on the right side of the page:
Alter One Value: select:
You need to alter one value: Change the
select: 'single'
to
select: 'multi'
which will allow you to select numerous files to download to your computer. If you do not make that one change then you won't be able to choose more than one file in the File dialog.
Click the Run Button to Start
Next, you'll see a [Run] button to start the app (above the code sample).
Go ahead and click that button.
Pick Files For Download
After that just traverse through your skydrive files and choose more than one in a folder and click the [Open] button. At that point, you will see one of the files actually downloads, and a number of file names are displayed in the bottom (output) section of the example web page.
My Questions
Why is it that the others do not download, even though wl.download is called in the loop, just as the console.log is called in the loop?
Is this a known limitation of the browser?
Is this a known bug in skydrive API?
Is this just a bug in the example code?
The problem here is that the call to wl.download({ "path": file.id + "/content" }) stores some internal state (among other things, the file being downloaded and the current status thereof). By looping over the list of files, that state is in fact overwritten with each call. When I tried downloading three text files at once, it was always the last one that was actually downloaded and never the first two.
The difficulty here is that the downloads are executed in the traditional fashion, whereby the server adds Content-Disposition: attachment to the response headers to force the browser to download the file. Because of this, it is not possible to receive notification of any kind when the download has actually completed, meaning that you can't perform the downloads serially to get around the state problem.
One approach that I thought might work is inspired by this question. According to the documentation, we can get a download link to a file if we append /content?suppress_redirects=true to its id. Using this approach, we can set the src property of an IFrame and download the file that way. This works OK, but it will only force a download for file types that the browser can't natively display (zip files, Exe files, etc.) due to the lack of the Content-Disposition: attachment response header.
The following is what I used in the Interactive Live SDK.
WL.init({ client_id: clientId, redirect_uri: redirectUri });
WL.login({ "scope": "wl.skydrive wl.signin" }).then(
function(response) {
openFromSkyDrive();
},
function(response) {
log("Failed to authenticate.");
}
);
function openFromSkyDrive() {
WL.fileDialog({
mode: 'open',
select: 'multi'
}).then(
function(response) {
log("The following file is being downloaded:");
log("");
var files = response.data.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
log(file.name);
WL.api({
path: file.id + "/content?suppress_redirects=true",
method: "GET"
}).then(
function (response) {
var iframe = document.createElement("iframe");
iframe.src = response.location;
iframe.style.display = "none";
document.body.appendChild(iframe);
},
function (responseFailed) {
log("Error calling API: " + responseFailed.error.message);
}
);
}
},
function(errorResponse) {
log("WL.fileDialog errorResponse = " + JSON.stringify(errorResponse));
}
);
}
function log(message) {
var child = document.createTextNode(message);
var parent = document.getElementById('JsOutputDiv') || document.body;
parent.appendChild(child);
parent.appendChild(document.createElement("br"));
}
Did you try to bind some events to the WL.download() method? According to the documentation:
The WL.download method accepts only one parameter:
The required path parameter specifies the unique SkyDrive file ID of the file to download.
If the WL.download method call is unsuccessful, you can use its then method's onError parameter to report the error. In this case, the WL.download doesn't support the onSuccess and onProgress parameters. If the WL.download method call is successful, the user experience for actually downloading the files will differ based on the type of web browser in use.
Perhaps you are getting some errors in your log to identify the problem.
For me, one suggestion without having checked the documentation, I can think of the fact that you are not waiting for each download to end. Why not change your loop in such a manner that you call WL.download() only if you know no other download is currently running ( like calling the next WL.download only in the success/complete event ):
WL.download({ "path": file.id + "/content" }).then(
function (response) {
window.console && console.log("File downloaded.");
//call the next WL.download() here <!-----------------
},
function (responseFailed) {
window.console && console.log( "Error downloading file: " + responseFailed.error.message);
}
);

Is robust javascript-only upload of file possible

I want a robust way to upload a file. That means that I want to be able to handle interruptions, error and pauses.
So my question is: Is something like the following possible using javascript only on the client.
If so I would like pointers to libraries, tutorials, books or implementations.
If not I would like an explanation to why it's not possible.
Scenario:
Open a large file
Split it into parts
For each part I would like to
Create checksum and append to data
Post data to server (the server would check if data uploaded correctly)
Check a web page on server to see if upload is ok
If yes upload next part if no retry
Assume all posts to server is accompanied by relevant meta data (sessionid and whatnot).
No. You can, through a certain amount of hackery, begin a file upload with AJAX, in which case you'll be able to tell when it's finished uploading. That's it.
JavaScript does not have any direct access to files on the visitor's computer for security reasons. The most you'll be able to see from within your script is the filename.
Firefox 3.5 adds support for DOM progress event monitoring of XMLHttpRequest transfers which allow you to keep track of at least upload status as well as completion and cancellation of uploads.
It's also possible to simulate progress tracking with iframes in clients that don't support this newer XMLHTTPRequest additions.
For an example of script that does just this, take a look at NoSWFUpload. I've been using it succesfully for about few months now.
It's possible in Firefox 3 to open a local file as chosen by a file upload field and read it into a JavaScript variable using the field's files array. That would allow you to do your own chunking, hashing and sending by AJAX.
There is some talk of getting something like this standardised by W3, but for the immediate future no other browser supports this.
Yes. Please look at the following file -
function Upload() {
var self = this;
this.btnUpload;
this.frmUpload;
this.inputFile;
this.divUploadArea;
this.upload = function(event, target) {
event.stopPropagation();
if (!$('.upload-button').length) {
return false;
}
if (!$('.form').length) {
return false;
}
self.btnUpload = target;
self.frmUpload = $(self.btnUpload).parents('form:first');
self.inputFile = $(self.btnUpload).prev('.upload-input');
self.divUploadArea = $(self.btnUpload).next('.uploaded-area');
var target = $(self.frmUpload).attr('target');
var action = $(self.frmUpload).attr('action');
$(self.frmUpload).attr('target', 'upload_target'); //change the form's target to the iframe's id
$(self.frmUpload).attr('action', '/trnUpload/upload'); //change the form's action to the upload iframe function page
$(self.frmUpload).parent("div").prepend(self.iframe);
$('#upload_target').load(function(event){
if (!$("#upload_target").contents().find('.upload-success:first').length) {
$('#upload_target').remove();
return false;
} else if($("#upload_target").contents().find('.upload-success:first') == 'false') {
$('#upload_target').remove();
return false;
}
var fid = $("#upload_target").contents().find('.fid:first').html();
var filename = $("#upload_target").contents().find('.filename:first').html();
var filetype = $("#upload_target").contents().find('.filetype:first').html();
var filesize = $("#upload_target").contents().find('.filesize:first').html();
$(self.frmUpload).attr('target', target); //change the form's target to the iframe's id
$(self.frmUpload).attr('action', action); //change the form's
$('#upload_target').remove();
self.insertUploadLink(fid, filename, filetype, filesize);
});
};
this.iframe = '' +
'false' +
'';
this.insertUploadLink = function (fid, filename, filetype, filesize) {
$('#upload-value').attr('value', fid);
}
}
$(document).ready(event) {
var myupload = new Upload();
myupload.upload(event, event.target);
}
With also using PHP's APC to query the status of how much of the file has been uploaded, you can do a progress bar with a periodical updater (I would use jQuery, which the above class requires also). You can use PHP to output both the periodical results, and the results of the upload in the iframe that is temporarily created.
This is hackish. You will need to spend a lot of time to get it to work. You will need admin access to whatever server you want to run it on so you can install APC. You will also need to setup the HTML form to correspond to the js Upload class. A reference on how to do this can be found here http://www.ultramegatech.com/blog/2008/12/creating-upload-progress-bar-php/

Categories

Resources