I have a problem. any help is welcome.
I am developing a solution that need to use google apis like cloud storage, Drive, etc.
In the clasic profile page I have to upload picture profile. then I use a angularjs to post data to my appengine/php/yii
formPHP:
`
<?php
require_once 'google/appengine/api/cloud_storage/CloudStorageTools.php';
use google\appengine\api\cloud_storage\CloudStorageTools;
$options = [ 'gs_bucket_name' => 'seektrabajo' ];
$upload_url = CloudStorageTools::createUploadUrl('/perfiles/subirFotoPerfil', $options);
?>
<input type="file" name="file"
nv-file-select="uploader.uploadAll()"
uploader="uploader" />
`
and when submit send data to google and google to yii ajax service:
`
public function actionSubirFotoPerfil(){
$answer = array('answer'=>'Incompleted','serverFile'=>'sinfoto.png');
if(!empty( $_FILES )){
$filename = $_FILES['file']['name'];
$gs_name = $_FILES['file']['tmp_name'];
move_uploaded_file($gs_name, 'gs://seektrabajo/'.$filename);
$answer = array( 'answer' => 'File transfer completed','serverFile' => $filename);
}
echo json_encode( $answer );
Yii::app()->end();
}
`
the problem is that never save the file uploaded to my bucket on cloud storage.
this not work for development/local mode and either appengine deployed.
in appengine land, I get this error:
I just get permision to bucket:
gsutil acl ch -u user#gmail.com:FULL_CONTROL gs://seektrabajo
the user#gmail.com y get from my google console/ apis-autentications/ Email
![web browser console error][1]
http://i.stack.imgur.com/noKnh.png
Somebody have an idea?
Thanks.
Why are you uploading the files to a static handler then uploading them to GCS? Use createUploadUrl() to template the <form> you serve. Provide a callback URL on your own app to the function and once the file is uploaded, a request will go to your app with the rest of the parameters of the form, the metadata of the file, etc...
With the pattern you are using, there is not only the headache of trying to figure out how to stream the data correctly, there's the simple fact that you're handling all that read/write on your instance, which is surely not a good idea if you intend to use this in production code where you pay for the used resources.
In short, there's no reason to use this pattern. In a traditional web app, you would post the file form to your own server on a route and do like you are in your example code (minus forwarding to GCS), but with App Engine you need to think a little different than the traditional ways of web development on a single VM you purchase and host a PHP runtime on.
Related
I'm able to upload a file to my firebase storage bucket via nodejs using the firebase-admin but when I go to the firebase UI I cannot open the file. I noticed that uploaded files via firebase UI will have an access token automatically generated but no for files uploaded via nodejs.
I already tried several things like setting metadata with downloadtokens and making the file public after it is uploaded. None has worked.
How can I generate the access token via API call rather than having to go to hi and click generate token for each uploaded file?
Full answer as of July 2020:
It is possible to generate a uuid and use it as a token when uploading a file to Firebase Cloud Storage (aka Google Cloud Storage)
First import this package:
const uuidv4 = require('uuid/v4');
or
const { v4: uuidv4 } = require('uuid');
Then, generate a unique id before uploading the file:
const uuid = uuidv4();
Finally, upload your file:
The most important is to embed
metadata: { firebaseStorageDownloadTokens: uuid, }
in the metadata field of upload function
await admin.storage().bucket().upload(filePath, {
destination: thumbFilePathForUpload,
metadata: {
metadata: {
firebaseStorageDownloadTokens: uuid,
}
},
});
In order to check if it worked, click on the newly uploaded file directly from Firebase Console, you should have a blue, clickable link along your file. You can also find the access token under File Location, right under the preview.
For instance:
I use the below command and it is working perfectly
const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
metadata: { firebaseStorageDownloadTokens: uuid }
To clarify #Rawan-25's answer further, what you want is:
bucket.upload(filename, {
destination,
metadata: {
metadata :{
firebaseStorageDownloadTokens: uuidv4(),
}
},
})
This is per this Github issue.
It's currently not possible to generate an access token with the Firebase Admin SDK. You'll have to do it using one of the client SDKs with the getDownloadUrl method on the StorageReference object. The token is only really intended for use with Firebase client apps.
However, the fact that you can't load a preview in the Firebase console for files uploaded with the Admin SDK is a known issue, and not the way that the console was intended to work. The Firebase team knows about this, but you should still file a bug report anyway with Firebase support to know them know you are impacted by the issue.
after an extensive search I managed to get the answer through a reddit post that referred to another stack overflow post lol.
Please take a look at answer #2! Get Download URL from file uploaded with Cloud Functions for Firebase
I am using the Google Sheets API with PHP and reading a sheet, I need to find a row and update its content.
I am currently iterating over the rows, looking for the value, but as the sheet grows, this seems rather inefficient. Is there a way to search for a cell, to retrieve the row, so I can then update?
My code to iterate is as follows.
$spreadsheet = (new Google\Spreadsheet\SpreadsheetService)
->getSpreadsheetFeed()
->getById("xxx sheet id xxx");
$worksheets = $spreadsheet->getWorksheetFeed()->getEntries();
$worksheet = $worksheets[0];
$CellFeed = $worksheet->getCellFeed();
foreach ($CellFeed->getEntries() as $E)
{
$r = $E->getRow();
/* ...... */
}
I believe your goal as follows.
You want to search a value from the specific column in the Spreadsheet and want to retrieve the row numbers of searched rows.
You want to achieve this using PHP.
Issue and workaround:
In that case, unfortunately, when Sheets API is used, in the current stage, it is required to do the following flow.
Retrieve all values from the sheet you want to search.
Retrieve the row and column numbers from the retrieved values.
This might be the same with your current script. Because in the current stage, there are no methods for directly searching the values in Sheets API. So in this answer, as a workaround, I would like to propose to use Web Apps created by Google Apps Script. When Google Apps Script is used, the searched row numbers can be retrieved by the TextFinder which is the built-in method. And the process cost of TextFinder is low. So I proposed it.
Usage:
Please do the following flow.
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
It is required to put this Google Apps Script project to the same Google Drive of the Spreadsheet you want to use.
2. Prepare script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.
function doGet(e) {
const sheet = SpreadsheetApp.openById(e.parameter.spreadsheetId).getSheetByName(e.parameter.sheetName);
const res = sheet.getRange(1, 2, sheet.getLastRow()).createTextFinder(e.parameter.searchValue).findAll().map(r => r.getRow());
return ContentService.createTextOutput(JSON.stringify({rowNumbers: res})).setMimeType(ContentService.MimeType.JSON);
}
3. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Select "Anyone, even anonymous" for "Who has access to the app:".
In this case, no access token is required to be request. I think that I recommend this setting for testing this workaround.
Of course, you can also use the access token. When you use the access token, please include one of scopes for Drive API like https://www.googleapis.com/auth/drive.readonly.
And also, I think that a key value can be used as the query parameter instead of the access token.
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
4. Testing Web Apps using PHP script.
Please set the URL of your Web Apps to the following script. And, please set the spreadsheet ID, sheet name. From your replying, in this sample, the search value and column number are Pj/5678 and 2, respectively. 2 of searchColumn means the column "B".
<?php
$url = 'https://script.google.com/macros/s/###/exec'; // Please set the URL of Web Apps.
$q = array(
'spreadsheetId' => '###', // Please set the Spreadsheet ID.
'sheetName' => 'Sheet1',
'searchValue' => 'Pj/5678',
'searchColumn' => 2
);
$curl = curl_init();
$option = [
CURLOPT_URL => $url . '?' . http_build_query($q),
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($curl, $option);
$res = curl_exec($curl);
$obj = json_decode($res);
print_r($obj);
curl_close($curl);
?>
Result:
When above script is run, the following value is returned. The row numbers of searched rows are returned.
{"rowNumbers":[###, ###,,,]}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Class TextFinder
I'm upload multiple images inside Picture(like folder) under Google Cloud Storage bucket.
Eg. Picture/a.jpg, Picture/b.jpg, Pictuer/c.jpg, .....
Now, I want to directly show those multiple images from Cloud Storage into my cakephp 2.x web application as a list.
According to Google Cloud Storage documentation, to access each object inside bucket, Signed URLs must be generated.
So, I created signed URLs for each object based on my selecting data from database. Following is my sample code.
<?php
# Imports the Google Cloud client library
use Google\Cloud\Storage\StorageClient;
use Google\Cloud\Core\Exception\GoogleException;
function index() {
//$rsl = 'select data from database';
for($i=0; $i<$count; $i++) {
# create url to access image from google cloud storage
$file_path = $rsl[$i]['pic']['file_path'];
if(!empty($file_path)) {
$rsl[$i]['pic']['real_path'] = $this->get_object_v4_signed_url($file_path);
} else {
$rsl[$i]['pic']['real_path'] = '';
}
}
}
/**
* Generate a v4 signed URL for downloading an object.
*
* #param string $bucketName the name of your Google Cloud bucket.
* #param string $objectName the name of your Google Cloud object.
*
* #return void
*/
function get_object_v4_signed_url($objectName) {
$cloud = parent::connect_to_google_cloud_storage();
$storage = $cloud[0];
$bucketName = $cloud[1];
$bucket = $storage->bucket($bucketName);
$object = $bucket->object($objectName);
if($object->exists()) {
$url = $object->signedUrl(
# This URL is valid for 1 minutes
new \DateTime('1 min'),
[
'version' => 'v4'
]
);
} else {
$url = '';
}
return $url;
}
?>
The problem is, it takes too long to generate because each file is generated to v4 signed URL. When I read Google Cloud Storage documentation, I didn't see to generate v4 signed URL of multiple objects at once(may be I'm wrong). So, is there anyway to speed it up this generating process?
As you mention there is no explanation in the Google Cloud Storage documentation on how to generate signed URLs for multiple objects using PHP as you do. However, I have found that using the gsutil signurl command, you can specify multiple paths or even use a wildcard:
gsutil signurl -d 1m ~/sandbox/key.json gs://bucket_name/object_1.jpg gs://bucket_name/object_2.jpg ...
gsutil signurl -d 1m ~/sandbox/key.json gs://bucket_name/*
This is specified in the gsutil help signurl page: "Multiple gs:// urls may be provided and may contain wildcards. A signed url will be produced for each provided url, authorized for the specified HTTP method and valid for the given duration."
Another option could be using multi-threading in your APP, here you'll find how to perform multi-threading using the pthreads API. If you choose to use this API you should bear in mind (from the documentation):
Warning
The pthreads extension cannot be used in a web server environment. Threading in PHP is therefore restricted to CLI-based applications only.
Warning
pthreads (v3) can only be used with PHP 7.2+: This is due to ZTS mode being unsafe in 7.0 and 7.1.
But since you have a web APP, I think that neither of those solutions would be useful for you. Besides that you could try using Cloud Functions to generate the signed URLs.
I am pretty new to using Guzzle with Laravel. I currently just use it for communication between my front-end and a seperate REST api.
I'm trying to download a file from my api but I'm not sure how to go about it. I could just specify the path to the file and directly download it but I also want to be able to stream or view it in my browser so a user can just view a word document instead of just downloading it.
Currently I'm sending a GET request from front end project (with backend to do api calls) to the api project:
$resp = $client->request('GET', env('API_BASE_URL').'/project/'.$id. '/download-report', [ 'headers' => [ 'Authorization' => 'Bearer '. session()->get('api_token') ] ]);
and in my api backend I return the file with the ->download() function.
return response()->download($report->getPath());
Can someone explain what would be the best way to approach this situation?
Solutions for both issues would be awesome, just downloading it, or actually viewing the contents of the word document.
Thanks in advance!
First of all, it's better to serve files with the web server (and leave PHP for "smarter" work). The idea behind it is to generate a "secure" link, that hides the real file and has an expiration timeout.
There are a few ways to do that (depends on your server: nginx, Apache or something else). A good example for nginx, with which you can just generate such link in your API, and send it to the end user through your frontend.
If you prefer to do in PHP for some reasons, just download the file to a temporary file and send it using response()->download() with corresponding headers (Content-Type) in your frontend.
$tmpFile = tempnam(sys_get_temp_dir(), 'reports_');
$resp = $client->request('GET', '...', [
// ... your options ...
'sink' => $tmpFile,
]);
response()->download(
$tmpFile,
null,
[
'Content-Type' => $resp->getHeader('Content-Type')[0]
]
)->deleteFileAfterSend(true);
I am having issue with the Google Drive API, i was able to fetch the files using API But i can't download via this link. I guess, must some auth, but i have used refresh tokens to authenticate.Please see below for my code
$this->load->library('google-api-php-client/src/Google_Client');
include APPPATH . '/libraries/google-api-php-client/src/contrib/Google_DriveService.php';
// Library Files Configuration to get access token and Refresh Token
$client = new Google_Client();
$client->setAccessType('offline'); // default: offline
$client->setApplicationName('xxx'); //name of the application
$client->setClientId('yyyy'); //insert your client id
$client->setClientSecret('zzz'); //insert your client secret
$client->setScopes(array('https://www.googleapis.com/auth/drive'));
$service = new Google_DriveService($client);
$client->refreshToken($drive_data->refreshtoken);
$client->getAccessToken();
$parameters = array();
$files = $service->files->listFiles($parameters);
foreach ($files['items'] as $key => $items)
{
Download
}
Anybody knows how to get the download url with authentication?
This is having the answer:
(Java) Download URL not working
There seem to be some changes on v2 of GDrive, instead of using "downloadUrl" you may have to use "webContentLink" for getting the download link
To get downloadUrls, you need to get the metadata of a file. You can do so by using the get method. The method will return a File Resource representation. In this resource, there is a downloadUrl property. If you're able to access the files and get the URL already, then there should be no problem with your authentication setup. There could be permission issues where you may not have access to certain drive files, but if you receive no error for it, you should be fine there too. I am not particularly familiar with PHP, but perhaps you are not downloading it correctly? Here it seems to be done differently.
I also suggest that you check out the Drive PHP Quickstart App to use as a reference.
I have bumped into the same problem today and just found a solution for my case. I hope that this helps the one or another confused PHP coder out there who also does not get a downloadUrl. I assume that you are working with the examples of the v2 API, as seen on https://developers.google.com/drive/v2/reference.
First, I have altered the head to not only access the metadata but get full access (mind the DRIVE constant):
<?php
require 'vendor/autoload.php';
const DRIVE = "https://www.googleapis.com/auth/drive";
define('APPLICATION_NAME', 'MAGOS poller');
define('CREDENTIALS_PATH', 'credentials.json');
define('CLIENT_SECRET_PATH', 'client_secret.json');
define('SCOPES', implode(' ', array(Google_Service_Drive::DRIVE)));
Then I have deleted my credentials file (credentials.json) and reran the script so it authenticated once more against gDrive and recreated the credentials file. After that
$downloadUrl = $file->getDownloadUrl();
finally worked like a charm.