How to get a blob from selected files on GooglePicker - google-api

I am using GooglePicker with React, and the result I am getting is an array of objects...
[
{
"id": "1...m",
"serviceId": "docs",
"mimeType": "image/jpeg",
"name": "name.jpg",
"description": "",
"type": "photo",
"lastEditedUtc": 1575388407136,
"iconUrl": "https://drive-thirdparty.googleusercontent.com/16/type/image/jpeg",
"url": "https://drive.google.com/file/d/1...m/view?usp=drive_web",
"embedUrl": "https://drive.google.com/file/d/1...m/preview?usp=drive_web",
"sizeBytes": 111364,
"rotation": 0,
"rotationDegree": 0,
"parentId": "0...A"
}]
So I tried to access through https://www.googleapis.com/drive/v3/files and directly through file.url using
const fetchOptions = { headers: { Authorization: `Bearer ${accessToken}` } };
docs.forEach((file) => {
...
fetch(file.url, fetchOptions).then((res) => {
const blob = res.blob();
uploadFile(blob);
});
});
But I get 403 or CORS; I tried setting the relayUrl in the picker, but this broke the Picker.
Notes:
I have these 3 scopes in my auth2:
['https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.readonly']```
I have my computer's url with port and protocol set as Authorized JavaScript origins and Authorized redirect URIs
Any ideas?
Edit 1:
I also tried using Google API like this:
const FILE_URL = 'https://www.googleapis.com/drive/v3/files';
const url = isDoc
? `${FILE_URL}/${file.id}/export?mimeType=${mimeType}`
: `${FILE_URL}/${file.id}?alt=media`;
fetch(url, fetchOptions).then((res) => {
const blob = res.blob();
uploadFile(blob);
});

You'll need the Drive API
From your question it seems that you are trying to do everything with Google Picker. However, the picker will only get you limited metadata for the files, so you can open them with your account (i.e. see them in another window) or let you upload files. If you want to download the actual file, then you will need to use the Drive API.
Drive Quickstart for browser JavaScript
The flow might be:
let user pick file
get metadata object
extract file id from object
make a call to Drive API (get with alt='media')
If I have misunderstood and you are already using the Drive API, then it would be helpful to see the associated code with that.
Ref
Quickstart
get
export)
EDIT:
Here is an example of using the Picker API to feed into the Drive API with gapi using the same login client.
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Google Picker Example</title>
</head>
<body>
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
<div id="result"></div>
<script type="text/javascript" src="script.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body>
</html>
JS
const API_KEY = 'AI...';
const CLIENT_ID = '44...';
const appId = "44...";
const SCOPES = ["https://www.googleapis.com/auth/drive"];
const DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/drive/v3/rest",
];
const authorizeButton = document.getElementById("authorize_button");
const signoutButton = document.getElementById("signout_button");
// Use the Google API Loader script to load the google.picker script.
function handleClientLoad() {
gapi.load("client:auth2:picker", initClient);
}
function initClient() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES[0]
})
.then(
function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(handleSignIn);
// Handle the initial sign-in state.
handleSignIn(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
},
function (error) {
appendPre(JSON.stringify(error, null, 2));
}
);
}
function handleSignIn(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = "none";
signoutButton.style.display = "block";
createPicker();
} else {
authorizeButton.style.display = "block";
signoutButton.style.display = "none";
}
}
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
function createPicker() {
const token = gapi.client.getToken().access_token
if (token) {
let view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes("image/png,image/jpeg,image/jpg");
let picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setAppId(appId)
.setOAuthToken(token)
.addView(view)
.addView(new google.picker.DocsUploadView())
.setDeveloperKey(API_KEY)
.setCallback(getFile)
.build();
picker.setVisible(true);
}
}
function getFile(pickerResp) {
gapi.client.drive.files
.get({
fileId: pickerResp.docs[0].id,
alt: 'media'
})
.then(resp => {
console.log("fetch response", resp.status)
let binary = resp.body
// EDIT - addition from Gabrielle vvvv
let l = binary.length
let array = new Uint8Array(l);
for (var i = 0; i<l; i++){
array[i] = binary,charCodeAt(i);
}
let blob = new Blob([array], {type: 'application/octet-stream'});
// EDIT - addition from Gabrielle ^^^^
}
This code is adapted from the Drive Quickstart and the Picker Quickstart.
Note - this does give an error in the console, but it does seem to work all the same. This does seem to be a bug with the Picker - https://issuetracker.google.com/177046274
EDIT from Gabrielle
Note - using get with alt = media is for binary files. To get sheets/docs/slides etc, you need to use the export end point.

Related

How to load Google API client library with SvelteKit

I'm new to SvelteKit and trying to find out how to load the Google client library for Javascript.
Google tells me to do it like this:
<head>
<script src="https://apis.google.com/js/api.js"></script>
<script>
function start() {
// Initializes the client with the API key and the Translate API.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
}).then(function() {
// Executes an API request, and returns a Promise.
// The method name `language.translations.list` comes from the API discovery.
return gapi.client.language.translations.list({
q: 'hello world',
source: 'en',
target: 'de',
});
}).then(function(response) {
console.log(response.result.data.translations[0].translatedText);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// Loads the JavaScript client library and invokes `start` afterwards.
gapi.load('client', start);
</script>
</head>
The problem is that SvelteKit doesn't allow 2 or more script tags on a page (I don't want it to be the layout page).
<script src="https://apis.google.com/js/api.js"></script>
<script>
import { onMount } from 'svelte';
gapi.client.init({...
</script>
This results in follwing error message:
A component can only have one instance-level <script> element
As my intention is to create a progressive web app (PWA) using Workbox I don't want to import the Google library as described here because the package containing this library would become too heavy.
Any ideas how to load the Google client library? Maybe there's a Workbox way to do it? Couldn't find a SvelteKit example on Google or YouTube.
Thanks in advance
The svelte:head tag allows you to add resources to the document head when a component is loaded. This example should work:
<script>
const start = async () => {
// Initializes the client with the API key and the Translate API.
// #ts-ignore
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
}).then(function() {
// Executes an API request, and returns a Promise.
// The method name `language.translations.list` comes from the API discovery.
return gapi.client.language.translations.list({
q: 'hello world',
source: 'en',
target: 'de',
});
}).then(function(response) {
console.log(response.result.data.translations[0].translatedText);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
const initializeGapi = async () => {
gapi.load('client', start);
}
</script>
<svelte:head>
<script src="https://apis.google.com/js/api.js" on:load={initializeGapi}></script>
</svelte:head>
I've made something like this.
Save it as GoogleMap.svelte to your lib folder. and use it like this;
<GoogleMap
{map}
globally
on:load={() => {
console.log('MAP SAYS IM LOADED');
}}
/>
Map is a reference object
globally defines it to window.map
<script>
import { onMount } from 'svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
//import mapStyles from './map-styles'; // optional
export let globally = false;
export let map;
let src = '';
const key = '';
// #ts-ignore
let container;
let zoom = 8;
let center = { lat: 37.5742776, lng: 43.7260158 };
onMount(() => {
Object.assign(window, {
mapLoaded: () => {
// #ts-ignore
map = new google.maps.Map(container, {
zoom,
center
// styles: mapStyles
});
dispatch('load', true);
if (globally) {
Object.assign(window, { map });
}
}
});
//Assign
src = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=mapLoaded`;
});
</script>
<!-- This is tailwind css class change with whatever fits to your case. -->
<div class="w-full h-full" bind:this={container} />
<svelte:head>
{#if src}
<script {src}></script>
{/if}
</svelte:head>

How can I create a pdf for download in blazor wasm

In a blazor wasm, I want to create a pdf and make it downloadable. I tested pdfflow, but was only able to make it run in a console application. There are several commercial solutions (devexpress, syncfusion), but they are really expensive.
Next, I stepped over the blog post Generate PDF files using an html template and Playwright, that seemed much promising to me. Unfortunately, it does not contain a complete example and I cannot make it run. Here is, what I tried:
I added template.html to wwwroot folder.
template.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="invoice.css">
</head>
<body style="padding: 3rem">
<h1>Invoice</h1>
Awesome company<br />
</body>
</html>
I added a button, that invokes CreatePdf(). I copied the code from the link above and modified it according to my needs (and because e. g. there is no method LoadOrder(orderId) provided).
Index.razor
#page "/"
#using Scriban
#using System.IO
#using Microsoft.Playwright
#inject HttpClient Http
<button class="btn btn-primary" #onclick="CreatePdf">pdf</button>
#code{
private async Task CreatePdf()
{
//var templateContent = File.ReadAllText("template.html");
var templateContent = await Http.GetStringAsync("template.html");
var template = Template.Parse(templateContent);
//var templateData = new { Invoice = LoadOrder(orderId) };
//var pageContent = template.Render(templateData);
var pageContent = "testString";
//var dataUrl = "data:text/html;base64," + Convert.ToBase64String(Encoding.UTF8.GetBytes(pageContent));
var dataUrl = "data:text/html;base64," + pageContent;
I added the following two lines, because "browser" was not declared (Source from playwright).
// Here, the app crashes.
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Webkit.LaunchAsync();
await using var context = await browser.NewContextAsync();
var page = await context.NewPageAsync();
await page.GotoAsync(dataUrl, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });
var output = await page.PdfAsync(new PagePdfOptions
{
Format = "A4",
Landscape = false,
});
await File.WriteAllBytesAsync("output.pdf", output);
} }
How can i make above code run?
You cannot use Playwright from a browser (js / wasm). So, you have to use another solution. For instance, you can use jsPDF. This library is not perfect when converting html to pdf, but maybe it will be ok for your usage.
Add a few script references in index.html to use jsPDF (you can also install them using npm if you prefer)
<script src="_framework/blazor.webassembly.js"></script>
<!-- jsPDF references -->
<script src="https://unpkg.com/jspdf#latest/dist/jspdf.umd.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify#2.3.3/dist/purify.min.js"></script>
Create a file wwwroot/HtmlToPdf.js with the following content:
export function generateAndDownloadPdf(htmlOrElement, filename) {
const doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'a4'
});
return new Promise((resolve, reject) => {
doc.html(htmlOrElement, {
callback: doc => {
doc.save(filename);
resolve();
}
});
});
}
export function generatePdf(htmlOrElement) {
const doc = new jspdf.jsPDF();
return new Promise((resolve, reject) => {
doc.html(htmlOrElement, {
callback: doc => {
const output = doc.output("arraybuffer");
resolve(new Uint8Array(output));
}
});
});
}
Then, you can use the script from a Razor component:
#inject IJSRuntime JSRuntime
<button type="button" #onclick="DownloadPdf">Generate</button>
#code {
async Task DownloadPdf()
{
await using var module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./HtmlToPdf.js");
// Generate and download the PDF
await module.InvokeVoidAsync("generateAndDownloadPdf", "<h1>sample</h1>", "sample.pdf");
// Generate the PDF and get its content as byte[] (need .NET 6 to support Uint8Array)
var bytes = await module.InvokeAsync<byte[]>("generatePdf", "<h1>sample</h1>");
}
}

FIXED: SailsJs + Google Blogger is timing out

So I've been tinkering with SailsJs and really like it so far, but I am trying to pull from posts from a blog I own into a view. This is a bit of a problem because the connection times out when trying to get the index view, and there is no feedback from the console via the console.log entries.
Blogger Service
// BloggerService.js - in api/services
var g = require('googleapis');
var apiKey = 'OUche33eekym0nKEY-uME';
exports.getBlogPosts = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb = null;
return console.log(err);
} else {
var opts = { 'blogId': options.id, 'maxResults': options.limit, 'fields': 'items(title,content,url)' };
cb = client.blogger.posts.list(opts);
};
});
};
exports.getBlogPost = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb = null;
return console.log(err);
} else {
var opts = { 'blogId': options.id, 'postId': options.postId };
cb = client.blogger.posts.get(opts);
};
});
};
Calling the service in the controller. Frustrating because the bottom of the documentation has a very cavalier way of saying where/how the service is called.
BlogController.js
/**
* BlogController.js
*
* #description ::
* #docs :: http://sailsjs.org/#!documentation/controllers
*/
module.exports = {
index: function(req, res){
BloggerService.getBlogPosts({'id':'86753098675309','limit':6},function(err, blogPosts){
if(err){
return console.log(err);
} else {
console.log(blogPosts.items[0].url);
res = blogPosts;
};
});
}
}
Index view
<div>
<% _.each(Model.items, function (blogPost) { %>
<div class="panel panel-default">
<div class="panel-heading"><%= blogPost.title %></div>
<div class="panel-body"><%= blogPost.content %><input type="hidden" value="<%= blogPost.id %>"></div>
</div>
<% }) %>
</div>
Any help would be greatly appreciated. Thank you for the time you spent looking at this.
UPDATE
Many thanks to Scott, who got me closer to the end results. This is what I have thus far, but just need to clear up an authentication issue with discover/apiKeys.
exports.getBlogPosts = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb(err);
} else {
var opts = { 'blogId': options.id, 'maxResults': options.limit, 'fetchBodies': false, 'fields': 'items(title,url)' }
client.blogger.posts.list(opts).withApiKey(apiKey).execute(cb);
};
});
};
exports.getBlogPost = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb(err);
} else {
var opts = { 'blogId': options.id, 'postId': options.postId };
client.blogger.posts.get(opts).withApiKey(apiKey).execute(cb);
};
});
};
It looks like you might be new to Node.js, so you might want to read up on asynchronous programming with Node and the req and res objects used by Express. The two problems I see with your code right off the bat are:
That you're assigning a value to your callback in BloggerService.js, rather than actually calling the callback: cb = client.blogger.posts.list(opts) should be (based on a quick scan of the Google API docs) client.blogger.posts.list(opts).execute(cb), and in case of an error cb = null should be cb(err).
You're assigning a value to the response object, instead of sending a response: res = blogPosts should be res.json(blogPosts).
As far as where / when you call your service, the docs aren't intending to be cavalier. The services are globalized so that they can be called from anywhere within any controller; it's up to you as the developer to decide where you need your service calls to be!

Youtube API v3 - Select menu to access public channel video data without Oauth

I want to access and view public Youtube videos (simple read only) from any Youtube channel without resorting to Oauth, just with plain API key. I haven't found a decent layman example on how to go about with API v3 ;-(
I have this to juggle with which I cannot get to work. Basically, a Select menu contains options whose values are existing channel IDs. When an option containing a channel ID is selected, it should trigger requestUserUploadsPlaylistId(). Then, when NEXTbutton or PREVIOUSbutton are activated, function requestVideoPlaylist() would kick in. Is there a better way to do this? I get the following error messages in Firebug:
TypeError: response.result is undefined (When I choose an option from SELECTmenu).
TypeError: response.result is undefined (After I click on buttons).
Here is what I am struggling with (am new to API v3 and kinda used to API v2 (sigh)):
<HTML is here>
script>
$('#NEXTbutton').prop('disabled', true).addClass('disabled');
</script>
<script type="text/javascript" src="https://apis.google.com
/js/client.js?onload=onJSClientLoad"></script>
<script>
var dd, playlistId, nextPageToken, prevPageToken;
function onJSClientLoad() {
gapi.client.setApiKey('YOUR-API-KEY');
gapi.client.load('youtube', 'v3', function(){
$('#NEXTbutton').prop('disabled', false).removeClass('disabled');
});
}
// Calling the following function via selected option value of select menu
// I am using "mine: false," since it's an unauthenticated request ??
function requestUserUploadsPlaylistId() {
var dd = $("#SELECTmenu option:selected").val();
var request = gapi.client.youtube.channels.list({
mine: false, // is this legit?
channelId: dd, // Variable is preset chosen value of SELECTmenu options
part: 'contentDetails,id'
});
request.execute(function(response) {
playlistId = response.result.items[0].contentDetails.relatedPlaylists.uploads;
channelId = response.result.items[0].id;
});
}
function requestVideoPlaylist(playlistId, pageToken) {
var requestOptions = {
playlistId: playlistId,
part: 'snippet,id',
maxResults: 5
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function(response) {
// Only show the page buttons if there's a next or previous page.
nextPageToken = response.result.nextPageToken;
var nextVis = nextPageToken ? 'visible' : 'hidden';
$('#NEXTbutton').css('visibility', nextVis);
prevPageToken = response.result.prevPageToken
var prevVis = prevPageToken ? 'visible' : 'hidden';
$('#PREVIOUSbutton').css('visibility', prevVis);
var playlistItems = response.result.items;
if (playlistItems) {
$.each(playlistItems, function(index, item) {
displayResult(item.snippet);
});
} else {
$('#CONTAINER').html('Sorry, no uploaded videos available');
}
});
}
function displayResult(videoSnippet) {
for(var i=0;i<response.items.length;i++) {
var channelTitle = response.items[i].snippet.channelTitle
var videoTitle = response.items[i].snippet.title;
var Thumbnail = response.items[i].snippet.thumbnails.medium.url;
var results = '<li><div class="video-result"><img src="'+Thumbnail+'" /></div>
<div class="chantitle">'+channelTitle+'</div>
<div class="vidtitle">'+videoTitle+'</div></li>';
$('#CONTAINER').append(results);
}
}
function nextPage() {
requestVideoPlaylist(playlistId, nextPageToken);
}
function previousPage() {
requestVideoPlaylist(playlistId, prevPageToken);
}
$('#NEXTbutton').on('click', function() { // Display next 5 results
nextPage();
});
$('#PREVIOUSbutton').on('click', function() { // Display previous 5 results
previousPage();
});
$("#SELECTmenu").on("change", function() {
$('#CONTAINER').empty();
if ($("#SELECTmenu option:selected").val().length === 24) { //Channel ID length
requestUserUploadsPlaylistId();
} else {
return false;
}
});
I'm surely missing something here, any pointers will be greatly appreciated.
FINAL UPDATE
A few updates later and I've finally answered my question after playing with the awesome Google APIs Explorer tool. Here is a sample working code allowing access to Youtube channel video-related data from a Select menu for read-only without using OAUTH, just an API key. The Select menu, based on a selected option's value (which contains a channel id), posts a video thumbnail, the thumbnail's channel origin; and the video's title. Should be easy to make the thumbnail clickable so as to load video in iframe embed or redirect to Youtube page. Enjoy!
// Change values and titles accordingly
<select id="SELECTmenu">
<option value="selchan">Select channel ...</option>
<option value="-YOUR-24digit-ChannelID-">Put-channel-title-here</option>
<option value="-YOUR-24digit-ChannelID-">Put-channel-title-here</option>
</select>
<button id="NEXTbutton">NEXT</button>
<button id="PREVIOUSbutton">PREV</button>
<ol id="CONTAINER"></ol> // Loads video data response
<script type="text/javascript"
src="https://apis.google.com/js/client.js?onload=onJSClientLoad">
</script>
var playlistId, nextPageToken, prevPageToken;
function onJSClientLoad() {
gapi.client.setApiKey('INSERT-YOUR-API-KEY'); // Insert your API key
gapi.client.load('youtube', 'v3', function(){
//Add function here if some action required immediately after the API loads
});
}
function requestUserUploadsPlaylistId(pageToken) {
// https://developers.google.com/youtube/v3/docs/channels/list
var selchan = $("#SELECTmenu option:selected").val();
var request = gapi.client.youtube.channels.list({
id: selchan,
part: 'snippet,contentDetails',
filter: 'uploads'
});
request.execute(function(response) {
playlistId = response.result.items[0].contentDetails.relatedPlaylists.uploads;
channelId = response.result.items[0].id;
requestVideoPlaylist(playlistId, pageToken);
});
}
function requestVideoPlaylist(playlistId, pageToken) {
$('#CONTAINER').empty();
var requestOptions = {
playlistId: playlistId,
part: 'snippet,id',
maxResults: 5 // can be changed
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function(response) {
// Only show the page buttons if there's a next or previous page.
nextPageToken = response.result.nextPageToken;
var nextVis = nextPageToken ? 'visible' : 'hidden';
$('#NEXTbutton').css('visibility', nextVis);
prevPageToken = response.result.prevPageToken
var prevVis = prevPageToken ? 'visible' : 'hidden';
$('#PREVIOUSbutton').css('visibility', prevVis);
var playlistItems = response.result.items;
if (playlistItems) {
displayResult(playlistItems);
} else {
$('#CONTAINER').html('Sorry, no uploaded videos.');
}
});
}
function displayResult(playlistItems) {
for(var i=0;i<playlistItems.length;i++) {
var channelTitle = playlistItems[i].snippet.channelTitle
var videoTitle = playlistItems[i].snippet.title;
var videoThumbnail = playlistItems[i].snippet.thumbnails.medium.url;
var results = '<li>
<div>'+channelTitle+'</div>
<div><img src="'+videoThumbnail+'" /></div>
<div>'+videoTitle+'</div>
</li>';
$('#CONTAINER').append(results);
}
}
function nextPage() {
$('#CONTAINER').empty(); // This needed here
requestVideoPlaylist(playlistId, nextPageToken);
}
function previousPage() {
$('#CONTAINER').empty(); // This needed here
requestVideoPlaylist(playlistId, prevPageToken);
}
$('#NEXTbutton').on('click', function() { // Display next maxResults
nextPage();
});
$('#PREVIOUSbutton').on('click', function() { // Display previous maxResults
previousPage();
});
// Using as filtering example Select option values which contain channel
// ID length of 24 alphanumerics/symbols to trigger functions just in case
// there are other option values in the menu that do not refer to channel IDs.
$("#SELECTmenu").on("change", function() {
$('#CONTAINER').empty();
if ($("#SELECTmenu option:selected").val().length === 24) {
requestUserUploadsPlaylistId();
return false;
} else {
return false;
}
});
NOTE:
Remember, code sample above is built based on what API v3 provided at the time of this posting.
TIP: It's better to make sure that the buttons be disabled during API call and re-enabled after API has posted the expected results. If you press those buttons while processing, you may get compounded and/or unexpected results. ~ Koolness

trying to save an image from Phonegap camera

I have this code which should get the base64 of the captured image, then save it as a jpg into the devices SD card, under the foler MyAppFolder. However it wont work and i cannot figure out why
<html>
<head>
<script src=../cordova.js></script>
<script>
// A button will call this function
//
function capturePhoto() {
sessionStorage.removeItem('imagepath');
// Take picture using device camera and retrieve image as base64-encoded string
navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50, destinationType: Camera.DestinationType.FILE_URI });
}
function onPhotoDataSuccess(imageURI) {
// Uncomment to view the base64 encoded image data
// console.log(imageData);
// Get image handle
//
var imgProfile = document.getElementById('imgProfile');
// Show the captured photo
// The inline CSS rules are used to resize the image
//
imgProfile.src = imageURI;
if(sessionStorage.isprofileimage==1){
getLocation();
}
movePic(imageURI);
}
// Called if something bad happens.
//
function onFail(message) {
alert('Failed because: ' + message);
}
function movePic(file){
window.resolveLocalFileSystemURI(file, resolveOnSuccess, resOnError);
}
//Callback function when the file system uri has been resolved
function resolveOnSuccess(entry){
var d = new Date();
var n = d.getTime();
//new file name
var newFileName = n + ".jpg";
var myFolderApp = "MyAppFolder";
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSys) {
//The folder is created if doesn't exist
fileSys.root.getDirectory( myFolderApp,
{create:true, exclusive: false},
function(directory) {
entry.moveTo(directory, newFileName, successMove, resOnError);
},
resOnError);
},
resOnError);
}
//Callback function when the file has been moved successfully - inserting the complete path
function successMove(entry) {
//Store imagepath in session for future use
// like to store it in database
sessionStorage.setItem('imagepath', entry.fullPath);
}
function resOnError(error) {
alert(error.code);
}
</script>
</head>
<body>
<button onclick="capturePhoto()">Take photo</button>
<img src="" id="imgProfile" style=position:absolute;top:0%;left:0%;width:100%;height:100%;>
</body>
</html>
when the button is pressed, the camera doesnt launch.
I solved it as follow. It might help you:
function capturePhoto() {
// Retrieve image file location from specified source
navigator.camera.getPicture(onPhotoSuccess, function(message) {
alert('Image Capture Failed');
}, {
quality : 40,
destinationType : Camera.DestinationType.FILE_URI
});
}
function onPhotoSuccess(imageURI) {
var gotFileEntry = function(fileEntry) {
alert("Default Image Directory " + fileEntry.fullPath);
var gotFileSystem = function(fileSystem) {
fileSystem.root.getDirectory("MyAppFolder", {
create : true
}, function(dataDir) {
var d = new Date();
var n = d.getTime();
//new file name
var newFileName = n + ".jpg";
// copy the file
fileEntry.moveTo(dataDir, newFileName, null, fsFail);
}, dirFail);
};
// get file system to copy or move image file to
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFileSystem,
onFail);
};
// resolve file system for image
window.resolveLocalFileSystemURI(imageURI, gotFileEntry, fsFail);
// file system fail
var onFail = function(error) {
alert("failed " + error.code);
};
var dirFail = function(error) {
alert("Directory" + error.code);
};
The button wont get clicked because your image src is overlapping it.
Change the position of image src and your code shall work fine

Resources