How to create url for each file entry in database (liferay) and download it - file

I have a table that contains a file id and a file content saved as clob type.
Now I want to create a link programmatically for each file and download via it.
How this is possible?
I have created my link but I have no idea how to get file from that link.
String fileUrl = themeDisplay.getPortalURL() + themeDisplay.getPathContext() + "/files/"
+ themeDisplay.getScopeGroupId() + "/Folder/" + HttpUtil.encodeURL(HtmlUtil.unescape(String.valueOf(fileId)));

I assume you have your own service for you custom table (Service created by Liferay Service Builder).
What you can do is that at the page load, you can create the <portlet:resourceURL> links with id as a parameter.
When user clicks this, the serveResource() method will be called. Here, just call your service and get the data by passing id. Take that data and write the data into OutputStream and provide the valid Content-Type.
This should work, as the main purpose of serveResource() is to serve resource! :)

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

Update an XML file in Google Drive via Google API

Let's say I have this minimal XML file on Google Drive.
<?xml version="1.0" encoding="UTF-8"?>
<MyCounter>
<counter>137</counter>
</MyCounter>
Using Google Script, I want to:
parse the XML
add 1 to the counter
update the file.
I'm at step 2 at the moment. I can delete the old file and create a new one with the same name and updated content. I prefer to update the existing one instead, so it will maintain the unique ID, and I can access the file with said ID instead of searching for it via file name.
You want to update a text file in your Google Drive without changing the file ID.
You want to add 1 to 137 of <counter>137</counter> in the file.
You want to achieve above using Google Apps Script.
If my understanding is correct, how about this sample script? I think that there are several answers for your situation. So please think of this as just one of them.
Flow:
Retrieve data from the text file.
Add 1 to number of <counter>{number}</counter> using replace().
Update the file using setContent().
By this flow, the file can be updated without changing file ID.
Sample script:
var fileId = "#####"; // Please set fileId here.
var file = DriveApp.getFileById(fileId);
var str = file.getBlob().getDataAsString();
var r = str.replace(/<counter>(\d+)<\/counter>/, function(_, p) {
return "<counter>" + (Number(p) + 1) + "</counter>";
});
file.setContent(r);
Note:
If you have already prepared the script for adding the counter, please use it.
References:
replace()
setContent()

Google Doc Download URL is null

I am trying to read the google doc using drive api. When I print the file metadata it prints as below:
[s~sakshumweb-hrd/3.370043974717039698].<stdout>: invite_friends_email:{"displayName":"Vivek Kumar","isAuthenticatedUser":true,"kind":"drive#user","permissionId":"13178633125197568962","picture":{"url":"https://lh5.googleusercontent.com/-4ElLv3j4-eI/AAAAAAAAAAI/AAAAAAAAAfQ/3b6TZenyTyA/s64/photo.jpg"}}
I 2013-09-06 19:35:41.489
[s~sakshumweb-hrd/3.370043974717039698].<stdout>: Download url is:null
The code printing it is as below:
System.out.println(file.getTitle() + ":" + file.getOwners().get(0) );
System.out.println("Download url is:" + file.getDownloadUrl());
Any idea why it comes null? Ultimately I want to read the file contents in my GAE for java code. So, if there is any other way to read then that would be fine too.
Look at the complete item metadata. If there is no download URL, it is usually because the document is a native Google Doc, in which case you should use exportLinks in place of downloadUrl. Another possibility is that you only have metadata scope, so don't have permission to access the content.

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

How to create file in current system from within Grails controller action and save data fetched from database?

In my Grails application I need to create a file in current system in which I need to save information fetched from table in database. How to do this from within controller action? I don't have any idea of it.
I have created file as
File file=new File("file name.txt")
file.createNewFile();
then I have wrote values of MySQL database table fields in it as:
file<<patient.id
file<<patient.name
.
.
.
it stores data like continuous text but I want to have a .doc file in which data should get stored in table. I found Apache's POI for creating doc file but I am not getting how it works and how I should use it.
Not sure exactly what you want to store in a file but below is an example of how to easly write a String to a file using Apache-commons-io Which should be included in grails
import org.apache.commons.io.FileUtils;
class SomeController{
def writeToFile = {
def data = getSomeStringData();
def fileStore = new File("./path/to/files/ControllerOutput_${new Date()}.txt");
fileStore.createNewFile();
FileUtils.writeStringToFile(fileStore, data);
println("your file was created # {fileStore.absolutePath} and is ${fileStore.length()} bytes");
}
}
Does this help? If not, you need to explain exactly what your looking for.
This is a comment to Michael's answer (unfortunately I still don't have the reputation to reply on answers).
If you're struggling around the problem how to specifiy the relative path from within your controller's context, this might help you:
So if you have following folder you want to read/write files from/into"..
/myproject/web-app/temp/
you can access the file like this:
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
// getResource references to the web-app folder as root folder
Resource resource = AH.getApplication().getParentContext().getResource("/temp/myfile.txt)

Resources