Google storage filenames must be prefixed with /gs/ - google-app-engine

I'm trying to use the code from this question getServingUrl() Method using Google Cloud Storage value
GcsFilename gcsFilename = new GcsFilename("bucketName", "objectName");
ImagesService is = ImagesServiceFactory.getImagesService();
String filename = String.format("/gs/%s/%s", gcsFilename.getBucketName(), gcsFilename.getObjectName());
String servingUrl = is.getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(filename));
I don't understand why there is /gs/ in the beginning of the path - it causes the code to produce a url that doesn't exist. but when I remove it I get 'java.lang.IllegalArgumentException: Google storage filenames must be prefixed with /gs/'
So back to the original question - how to get a serving url from a GCS file?
I can do
"http://storage.googleapis.com/"+ filename.getBucketName()+"/"+ filename.getObjectName()
but it breaks on local server, thus I assume is not the correct way.

It depends on how you want to access the file. If you access the file directly from the client (browser), then you can use a public URL:
"http://storage.googleapis.com/"+ filename.getBucketName()+"/"+ filename.getObjectName()
If you access this file internally - in your code, or call getServingUrl(), you use "/gs/" prefix. For example:
Image picture = ImagesServiceFactory.makeImageFromFilename("/gs/" + filename.getBucketName() + "/" + filename.getObjectName());
GcsFilename fileName = new GcsFilename(filename.getBucketName(), filename.getObjectName());
I think there is just a problem with the file name in your example.

Related

Drupal upload then move doesn't update path or filename in file list

I have a custom form using a "managed_file" which uploads to temp folder. Programmatically, I then load that file and move it to its permanent storage (overwriting any existing file with the* name) e.g.
// Upload file
$upfile = $this->entityTypeManager->getStorage('file')->load($fid);
// Source and destination
$sourceUri = $this->fileSystem->realpath($upfile->getFileUri());
$destinationUri = $this->fileSystem->realpath(\Drupal::config('system.file')->get('default_scheme') . "://") . '/x/y/z/XYZ_NEW.pdf';
// Move and overwrite
$this->fileSystem->move($sourceUri, $destinationUri, FileSystemInterface::EXISTS_REPLACE);
All of this works (i.e. the file physically is moved into the correct place with correct name); however, the file displayed in the listings (i.e. /admin/content/files) still shows the incorrect temporary folder as the URI.
Basically the file in the listings page seems to be showing the original filepath and name* of a previously successfully moved* file.
If I load this file with incorrect URI, i.e. using the incorrect temp path, the data loads, but then will not have a file info (as it doesn't exist. Also clicking the filename by browser under listings will show page not found and the URL showing the old URL i.e. /system/temporary?file=XYZ.pdf).
If I load this file by correct URI, i.e. using the correct destination path, file not found - same if I go to the path directly in the browser.
It appears the managed file doesn't know it was moved. How to resolve this bug?
The docs for FileSystem::move say "Moves a file to a new location without database changes or hook invocation."
So you are going to need to update the file entity with the new values..
Try this, untested:
// Upload file
$upfile = $this->entityTypeManager->getStorage('file')->load($fid);
// Source and destination
$sourceUri = $this->fileSystem->realpath($upfile->getFileUri());
$destinationUri = $this->fileSystem->realpath(\Drupal::config('system.file')->get('default_scheme') . "://") . '/x/y/z/XYZ_NEW.pdf';
// Move and overwrite
$newFileName = $this->fileSystem->move($sourceUri, $destinationUri, FileSystemInterface::EXISTS_REPLACE);
// Set the new file path on the file entity.
$upfile->setFileUri($newFileName);
// Set the file to permanent if needed.
$upfile->setPermanent();
// Save entity with changes.
$upfile->save();
I did not test this though.
You can check the functions on the file entity in the docs here
It turns out the class based methods do not update the database
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21File%21FileSystem.php/function/FileSystem%3A%3Amove/8.9.x
The procedural version does
https://api.drupal.org/api/drupal/core%21modules%21file%21file.module/function/file_move/8.9.x

read/download file using local absolute path

I have one file which is stored at D:/home/abc.pdf locally.
I have to read this file using AngularJs
var path="D:/home/abc.pdf";
var doc = document.createElement("a");
doc.href = path;
doc.download = path;
doc.click();
window.URL.revokeObjectURL(path);
I am not able to download this file.Giving error like Failed-Network error
That is impossible since local files are protected. Else you would be able to manipulate the hard drive as you wanted just by running a local HTML page.
So : if you want to get a file from the computer's hard drive, you have to use an <input> field and ask the user to upload the file using it.

Google Cloud Storage: download a file with a different name

I'm wondering if it's possible to download a file from Google Cloud Storage with a different name than the one that has in the bucket.
For example, in Google Cloud Storage I have stored a file named 123-file.txt but when I download it I would like choose a different name, let's say file.txt
I've noticed that the link for download it is like:
https://storage.cloud.google.com/bucket_name%2F123-file.txt?response-content-disposition=attachment;%20filename=123-file.txt
So I've tried to change it to:
https://storage.cloud.google.com/bucket_name%2F123-file.txt?response-content-disposition=attachment;%20filename=file.txt
But it still keeps downloading as 123-file.txt instead of file.txt
The response-content-disposition parameter can only be used by authorized requests. Anonymous links don't work with it. You have a few options:
The content-disposition of a particular object is part of its metadata and can be permanently set. If you always want a specific file to be downloaded with a specific name, you can just permanently set the content-disposition metadata for the object.
You can also generate signed URLs that include the response-content-disposition query parameter. Then the users will be making authorized requests to download the resource.
example (first option Brandon Yarbrough) with javascript library:
const storage = new Storage()
const fileBucket = storage.bucket('myBucket')
const file = fileBucket.file('MyOriginalFile.txt')
const newName = "NewName.txt"
await file.save(content, {
metadata: {
contentDisposition: `inline; filename="${newName}"`
}
})
the following is a part of a python script i've used to remove the forward slashes - added by google cloud buckets when to represent directories - from multiple objects, it's based on this blog post, please keep in mind the double quotes around the content position "file name"
def update_blob_download_name(bucket_name):
""" update the download name of blobs and remove
the path.
:returns: None
:rtype: None
"""
# Storage client, not added to the code for brevity
client = initialize_google_storage_client()
bucket = client.bucket(bucket_name)
for blob in bucket.list_blobs():
if "/" in blob.name:
remove_path = blob.name[blob.name.rfind("/") + 1:] # rfind gives that last occurence of the char
ext = pathlib.Path(remove_path).suffix
remove_id = remove_path[:remove_path.rfind("_id_")]
new_name = remove_id + ext
blob.content_disposition = f'attachment; filename="{new_name}"'
blob.patch()

Google Cloud Storage returning NoSuchKey for my public files

I have a public bucket here:
http://storage.googleapis.com/tripket1/
And all the files in this bucket have the ACL set to 'public-read'. Yet when I try to view any of the files, such as:
http://storage.googleapis.com/tripket1/2013-05-25%2019.17.32_150.jpg
it returns a 'NoSuchKey' error.
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
</Error>
What could be causing this problem? These files were uploaded using the GCS client library for Java. Here's a code snipped from the uploader:
GcsFilename thumbGcsFilename = new GcsFilename(bucketName, thumb_filename);
GcsFileOptions options = new GcsFileOptions.Builder().mimeType("image/" + photo_extension).acl("public-read").build();
GcsOutputChannel outputChannel = gcsService.createOrReplace(thumbGcsFilename, options);
outputChannel.write(ByteBuffer.wrap(newImageData));
outputChannel.close();
LOGGER.info("Wrote file");
String thumb_url_str = String.format("http://storage.googleapis.com/%s/%s", bucketName, thumb_filename);
return thumb_url_str;
You need to escape the % character in your object names.
For example, you have the following object:
gs://tripket1/2013-05-25%2019.17.32_150.jpg
Since you have a literal percent sign in your object's name, it must be escaped as %25 when URL encoded, so you can access the object with this URL:
http://storage.googleapis.com/tripket1/2013-05-25%252019.17.32_150.jpg
If you don't escape it, the %20 in your object name gets turned into a space () when being decoded at the server side, and it doesn't find an object name with a space in it.
Since it's a publicly readable bucket, I used gsutil to look at its contents, and I see the object you're trying to read is called
2013-05-25%2019.17.32_150.jpg
rather than
06b78005-4ad8-43d6-8fc5-bab867b653af/2013-05-25%2019.17.32_150.jpg

How to download a pdf file in Composite c1

I'm trying to implement a functionality that enables a user to download a PDF on clicking on a hyper-link. What i've done is, I've created a global datatype Publications which takes values "Description" and "PDF DOC" and I've a user control with a hyper-link which binds the description as its text.
LinkButton1.Text = details.Description;
Composite.Data.DataReference<IMediaFile> i = new Composite.Data.DataReference<IMediaFile>((details as A.DataTypes.Publications).PdfDoc);
string filePath = "/media(" + i.Data.Id + ")";
and on the click on the link button I've...
Response.ContentType = "Application/pdf";
Response.AppendHeader("Content-Disposition", "attachment; filename=Test_PDF.pdf");
Response.TransmitFile(filePath );
Response.End();
this is showing an error saying "could not find file", any idea why?
It looks like you are trying to use the C1 syntax for media files at a place where the C1 page renderer never replaces it with the actual url of the file. So you end up passing something like /media(b5354eba-3f69-4885-9eba-74576dff372d) to the Response.TransmitFile() function, which will not work because that is not a valid file path.
If you use this syntax on a C1 page, the page renderer will replace it with the real url of the file.
My advise would be to build this URL yourself and just link to it, instead of using TransmitFile. A simple redirect will suffice if the file is open for public access. If it is lying acessible on the web server already, there is not much point in using Response.TransmitFile and fetching it and writing it in the outputstream.
Try look at the DownloadFoldersAsZip package (https://bitbucket.org/burningice/compositec1contrib/src/4c31794cd46c/DownloadFoldersAsZip?at=default) which has this functionality. The main issue with your code is that you make the assumption of where the files are stored. You can't do that with the C1 Media Archive, since files can be either local, in a database, in Azure Blob storage or just a random place on the internet.
Instead you should use the GetReadStream() extension method of your IMediaFile instance. This will give you a stream which you can copy unto your Response.ResponseStream
See here for an example: https://bitbucket.org/burningice/compositec1contrib/src/4c31794cd46cb03dd0b0ee830b83204753355b03/DownloadFoldersAsZip/Web/GenerateZipHandler.cs?at=default#cl-145
solved it, just needed to give....string filePath = "~/media({" + i.Data.Id + "})"; instead of string filePath = "/media(" + i.Data.Id + ")";
You can also use this code
Composite.Data.DataReference i = new Composite.Data.DataReference((details as A.DataTypes.Publications).PdfDoc)
This gives the media file reference
string fileName = "/App_Data/Media/" + i.Data.Id.ToString();
this.Response.AddHeader("content-disposition", string.Format(
"attachment;filename=download.pdf", Path.GetFileName(fileName)));
this.Response.ContentType = "application/pdf";
this.Response.WriteFile(this.Server.MapPath(fileName));
This can get the file Downloaded as download.pdf

Resources