This is related to https://github.com/googleapis/google-api-nodejs-client/issues/1083
It seems with the latest versions of googleapis the package switched from using axios to gaxios - this has caused this error to resurface when using the example code on an Electron app.
How should this be handled?
googleapis
version: "googleapis": "^45.0.0",Invalid multipart request with 0 mime parts.
error upon requestHey @brobey8, it might also be worth opening a tracking issue on the Electron project. I believe that Electron essentially ships a vanilla version of Node.js, so I'm a little surprised this behavior would be working outside of Electron, but not when embedded in electron.
Out of curiosity, have you tried running the sample in isolation?
Hi @bcoe
it might also be worth opening a tracking issue on the Electron project
I can do that.
I believe that Electron essentially ships a vanilla version of Node.js, so I'm a little surprised this behavior would be working outside of Electron, but not when embedded in electron.
From the linked issue (and my limited understanding) it seems that Electron may be selecting the incorrect HTTP adapter - this was amendable in axios as you could manually set the adapter like so: google.options({adapter: require('axios/lib/adapters/http')})
, however now that the library has switched to gaxios I'm not sure if this is an option / how to do it.
I will give the sample a go in a node environment as well.
Out of curiosity, have you tried running the sample in isolation?
I just ran the sample using node from the cli with this code (which is almost identical to how I have it in my Electron app):
var fileName = '/Users/ben/Downloads/TRA3106.mpeg';
const fileSize = fs.statSync(fileName).size;
service.videos.insert(
{
part: 'id,snippet,status',
notifySubscribers: false,
requestBody: {
snippet: {
title: 'Node.js YouTube Upload Test',
description: 'Testing YouTube upload via Google APIs Node.js Client',
},
status: {
privacyStatus: 'private',
},
},
media: {
body: fs.createReadStream(fileName),
},
auth: auth
},
{
// Use the `onUploadProgress` event from Axios to track the
// number of bytes uploaded to this point.
onUploadProgress: evt => {
const progress = (evt.bytesRead / fileSize) * 100;
readline.clearLine(process.stdout, 0);
readline.cursorTo(process.stdout, 0, null);
process.stdout.write(`${Math.round(progress)}% complete`);
},
}
);
Was able to upload with no issues.
馃憤 it seems a bit weird that you need to select an http
adapter, is this setting the transport protocol to http
rather than https
?
Mind linking the electron issue when you open it?
@brobey8 did you manage to figure out the issue you were running into in Electron?
I have the same problem craeting the file in google drive if i use the script in electron but not outside node.
i tried the solution provided by @brobey8
var { google } = require('googleapis');
google.options({adapter: require('axios/lib/adapters/http')})
But now i get
```
Error
at createError (createError.js:15)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad [as __zone_symbol__ON_PROPERTYreadystatechange] (xhr.js:77)
at XMLHttpRequest.wrapFn (zone.js:1188)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:496)
at invokeTask (zone.js:1540)
at XMLHttpRequest.globalZoneAwareCallback (zone.js:1566)
@brobey8 could you share the full stack trace you receive when attempting to upload in electron?
Another question, is this upload code in a frontend component of your electron application? (so running in Chromium), or is it running in the Node.js context?
@bcoe my code is :
//creating file
const google = require('googleapis');
google.options({ adapter: require('axios/lib/adapters/http') })
drive.files.create({
resource: {
name: 'Another File 5',
mimeType: 'text/plain',
},
media: {
mimeType: 'text/plain',
body: 'File Body',
}
}, function(err,result){
if(err) console.log(err)
else console.log(result)
});
2:no i'm running it inside an angular component in electron
3:the error stack
GaxiosError: Invalid multipart request with 0 mime parts.
at Gaxios._request (http://localhost:4200/vendor.js:90110:23)
(anonymous) @ auth.service.ts:71
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
Promise.then (async)
scheduleMicroTask @ zone.js:578
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:410
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:252
scheduleResolveOrReject @ zone.js:862
resolvePromise @ zone.js:808
(anonymous) @ zone.js:724
Promise.then (async)
(anonymous) @ zone.js:1042
ZoneAwarePromise @ zone.js:891
Ctor.then @ zone.js:1041
createAPIRequest @ apirequest.js:43
create @ v3.js:658
(anonymous) @ auth.service.ts:61
step @ apps.service.ts:9
(anonymous) @ apps.service.ts:9
fulfilled @ apps.service.ts:9
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
Promise.then (async)
scheduleMicroTask @ zone.js:578
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:410
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:252
scheduleResolveOrReject @ zone.js:862
resolvePromise @ zone.js:808
(anonymous) @ zone.js:724
Promise.then (async)
(anonymous) @ zone.js:1042
ZoneAwarePromise @ zone.js:891
Ctor.then @ zone.js:1041
resolvePromise @ zone.js:775
(anonymous) @ zone.js:724
(anonymous) @ apps.service.ts:9
ZoneAwarePromise @ zone.js:891
step @ apps.service.ts:9
(anonymous) @ apps.service.ts:9
ZoneAwarePromise @ zone.js:891
push../src/app/services/auth.service.ts.__awaiter @ apps.service.ts:9
(anonymous) @ auth.service.ts:42
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:196
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next @ Subscriber.js:134
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next @ Subscriber.js:77
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
(anonymous) @ auth.service.ts:34
step @ apps.service.ts:9
(anonymous) @ apps.service.ts:9
(anonymous) @ apps.service.ts:9
ZoneAwarePromise @ zone.js:891
push../src/app/services/auth.service.ts.__awaiter @ apps.service.ts:9
(anonymous) @ auth.service.ts:30
readFileAfterClose @ fs.js:440
Hey there @brobey8 and @appfinder, I created a small testing app using Electron that seems to work for me. Please feel free to let me know if it's still not working for you, or if there's something in the recreation of the issue that we're missing. Thanks!
@appfinder perhaps you could submit a minimal patch to @sofisl's repository that exhibits the behavior you're seeing?
@bcoe and @brobey8 :
the create
function will work in node context but if you run it inside angular context inside an electron app it gives the error mentioned above
I created a simple demo repo
https://github.com/appfinder/googleapi-demo
based on this electron and angular repo
https://github.com/hover-hoss/angular-electron-adminlte
i only changed the code inside /src/app/home/home.component.ts
and /src/app/home/home.component.html
to test it
1: change
const clientId = "";
const clientSecret = "";
2: after running the app
use the app to login
Hey all. I was able to reproduce the issue successfully and found that, by changing media
type to multipart
, the file was uploaded successfully. Can you try that solution and see if it works for you? See code changed below:
drive.files.create({
resource: {
name: 'did it work?',
},
multipart: {
mimeType: 'text/plain',
body: 'File Body',
fields: 'id'
}
}, function (err, result) {
if (err) console.log(err)
else console.log(result)
});
@sofisl thank you for your response .. i confirm that it works by changing media
type to multipart
Hi all,
We dug into the issue a bit, and the solution I provided above still wasn't providing the file content in the upload. We've fixed this here and it will be available for our next release (early next week).
This solution unfortunately does not accept streams in the browser, so you will need to provide the entire file body in the upload. Note, for text files, you can simply set text/plain as the content type. For binary files, such as images, you will want to set the content type to image/jpg;base64 and base64 encode the upload body.
Alternatively, you can perform uploads from the Node.js context rather than the browser context without any restrictions.
Please let us know if this continues to cause issues!
Could someone help me pls?
I'm having the same error while trying to upload a video to Youtube using the API.
I have this code
const videoFilePath = 'D:/Videos/testup.mp4'
const videoFileSize = fs.statSync(videoFilePath).size
const videoTitle = 'Testao'
const videoDescription = 'oioioi'
const requestParameters = {
part: 'snippet, status',
requestBody: {
snippet: {
title: videoTitle,
description: videoDescription
}
},
media: {
mimeType: 'video/*',
body: fs.createReadStream(videoFilePath)
}
}
console.log('> [youtube-robot] Starting to upload the video to YouTube')
const youtubeResponse = await youtube.videos.insert(requestParameters, {
onUploadProgress: onUploadProgress
})
And I updated Google API to v4.1.1-beta.0 Pre-release with npm install googleapis-common@next
But I still get "Error: Invalid multipart request with 0 mime parts."
~sorry for my English
@Ribaas
Based this solution on the fact that upload is working as expected in the main Process as confirmed in https://github.com/googleapis/google-api-nodejs-client/issues/1878#issuecomment-620766201.
Alternatively, you can perform uploads from the Node.js context rather than the browser context without any restrictions.
Look into ElectronJs' Inter process communication modules (ipcMain). I have sent a Synchronous message from Renderer process with a data object containing the parameters necessary for the upload process. This synchronous message blocks the Renderer process until it gets a return value. Receive this data object on the Main process, call the function and return the response which will resume the Renderer process.
Also Read: ipc-sending messages
@kruzchy
So I've moved my code to the main process, and called my upload function using ipcMain, but now I get this error.
Error: The request does not include the video content.
Edit
I've got it working with this code:
const requestParameters = {
part: 'snippet, status',
requestBody: {
snippet: {
title: videoTitle,
description: videoDescription
}
},
media: {
body: fs.createReadStream(videoFilePath)
}
}
This is called from within an ipcMain listener:
ipcMain.on('ytUpload', async (event, arg) => {
console.log(arg)
const videoID = await ytUpload(arg)
event.returnValue = videoID
})
Thx for the help
Hi @Ribaas,
I think the best solution here is to either stream the video through the Node context, and not the browser context, as the library supports streaming only through the Node context. Alternatively, you could download the video to memory and then upload it to Youtube without using a stream. (As an aside, the nodejs-googleapis-common library was published today).
Beyond these two options, I think we'd be getting a bit too much into the Electron-specific realm, and you might want to open a bug with Electron referencing this issue.
Thanks!
@sofisl
How about inverting the assumption and changing the isBrowser function to a isNode function?
In the sense of:
function isBrowser() {
return typeof process === 'undefined';
}
Or a explicit option to force node execution. It should at least throw some error if you try to stream a file through the browser.
Detecting node.js or the browser is comically difficult. There are environments that like to include things like stubbed process
objects in webpacked code, and environments like to insert window
into node.js processes. You need to be really explicit about which context is being used here.
@JustinBeckwith
Since gaxios is used for the requests, it would make sense to check for node/browser in the same way gaxios does (currently checks for
window.fetch
and window.URL
). If the body is a ReadStream it always can be executed as node since it doesnt work in the browser anyways.
I guess the best course of action would be adding a execution environment setting to gaxios and using this to determine whether it should be executed as node/browser.