Problems with Resumable Upload For WebApps script - javascript

I was looking for a solution so that people with access to a spreadsheet could upload files through it, researching I found some solutions, but as I will need these people to upload videos through the spreadsheet, some solutions that used Blob ended up being discarded.
Searching, I found this script made by Tanaike, apparently it solves practically all problems, I thought of pulling it into the spreadsheet using an html alert, thus allowing people to upload files with sizes greater than 50mb.
The script can be found here:
Resumable Upload For WebApps
The issue is that I'm having some problems getting it to work, basically I'm getting this error when trying to upload a file:
Error: <HTML> <HEAD> <TITLE>Not Implemented</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>Not Implemented</H1> <H2>Error 501</H2> </BODY> </HTML>
Other than that, I have a few questions I'd like to clear up:
1- I'm not sure if with this script people with other accounts would be able to upload the files to my Google Drive, is it possible?
2- Is it possible implement it in a button on a spreadsheet and request that the file be uploaded in the same folder as that spreadsheet?
Sorry for the amount of questions, javascript and GAS are things that are not very present in my daily life, so I have a little difficulty.

Checking the developer console, the error returned from the server is accessNotConfigured. This happens when the GCP Project doesn't have the necessary APIs enabled. To fix this you need to create a new Cloud Project:
In the Google Cloud console, go to Menu > IAM & Admin > Create a Project.
In the Project Name field, enter a descriptive name for your project.
In the Location field, click Browse to display potential locations for your project. Then, click Select.
Click Create. The console navigates to the Dashboard page and your project is created within a few minutes.
After that you need to enable the Drive API:
In the Google Cloud console, go to Menu > More products > Google Workspace > Product Library.
Click the API that you want to turn on.
Click Enable.
Finally you need to attach the GCP project to your Apps Script Project:
Determine the Project number of your Cloud project.
Open the script whose Cloud project you want to replace.
At the left, click Project Settings.
Under Google Cloud Platform (GCP) Project, click Change project.
Enter the new project number and click Set project.
After attaching the standard project the error stopped showing up for me. The reason for this is that Google changed the way Apps Script creates GCP projects so now scripts may have Default or Standard projects. Default projects are essentially more restricted so you may have to create a Standard Project in certain scenarios. One these scenarios in the documentation is "To create a file-open dialog". Tanaike's code uses the same technique as the file-open dialogs to retrieve the access token from the server, which I believe is the cause of the error.
As for your other questions:
I'm not sure if with this script people with other accounts would be able to upload the files to my Google Drive, is it possible?
Only if you deploy it as a Web App, setting it to execute with your account and available to "Anyone with a Google account". This uses your account's access token to authorize so other users will upload to your account.
Is it possible implement it in a button on a spreadsheet and request that the file be uploaded in the same folder as that spreadsheet?
As I mentioned under 1., doing it within the spreadsheet probably won't work, but you can add the parents property to the request body on the HTML side to specify the folder. You can also retrieve it dynamically by calling google.script.run. Here's a sample I modified to do this:
google.script.run.withSuccessHandler(function(at) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
xhr.setRequestHeader('Authorization', "Bearer " + at.token);
xhr.setRequestHeader('Content-Type', "application/json");
xhr.send(JSON.stringify({
mimeType: fileType,
name: fileName,
parents: at.parent
}));
xhr.onload = function() {
doUpload({
location: xhr.getResponseHeader("location"),
chunks: chunks,
});
};
xhr.onerror = function() {
console.log(xhr.response);
};
}).getAt();
That's a part of the init() function in the index.html file. The at variable originally only received the access token retrieved from the server. I just modified it so it receives an object with both the access token and the folder ID, and I included the parent folder ID in the API call. You also need to modify the getAt() function in Code.gs to actually return the folder ID:
function getAt() {
var id = SpreadsheetApp.getActiveSpreadsheet().getId()
var folder = DriveApp.getFileById(id).getParents().next().getId()
return { token: ScriptApp.getOAuthToken(), parent: [folder] }
}
There's a lot to unpack here so I advise you to check the documentation. I think you'll have to go through the Web App route if you want other users to upload the files to your Drive.
Reference:
Web Apps
Communicate with Server Functions
Files.insert
Default and Standard Projects

Related

firebase functions upload file from web via stream only partially working

I am trying to create a firebase function that downloads a photo off the web via URL and uploads it to firebase storage.
Using Axios to stream the file to the bucket. it appears the file gets uploaded but i cant download or view it.
This is my code:
let fileURL = 'https://www.example.file.path.png'
let myFile = await axios.get(fileURL, { responseType: 'stream' })
let destFile = bucket.file(photoId).createWriteStream({ contentType: myFile.headers['content-type']})
myFile.data.pipe(destFile)
And here is the storage console from firebase:
I have messed around with the storage api and attempted using the upload and save functions. using axios get and write streams is the closest that I'v got to getting this to work.
Reading the example in the docs only aids in my confusion because the file is never reference in the upload function.. just the file name??
Feel like i'm almost there considering the file or rather the name of the file is there and the size and the type.. just not the content?
This problem is related with Firebase console only. There is no problem with downloading with command: gsutil cp gs://<your bucket>/yourfile . (doc) and as well it is working in Google Cloud Console in Storage browser (direct link).
However indeed file uploaded like this is not possible to be downloaded or previewed with Firebase console.
After many tries I have realized that this is related with custom metadata Access token, which underneath is calledfirebaseStorageDownloadTokens. It appears automatically if ex. you download the file directly in Firebase console.
Anyway I noticed that value of the metadata is irrelevant. According to my test if you change your createWriteStream method parameters to:
let destFile = bucket.file(photoId)
.createWriteStream({
metadata: {
contentType: myFile.headers['content-type'],
metadata: {
firebaseStorageDownloadTokens: 'canBeAnyThing'
}
}
});
The problem disappears...
A file already downloaded to Firebase Storage and affected by the issue can be fixed by adding the same metadata. In the screenshot you have provided you can see "File Location" if you open you will see link "Create new access token" or as well you can add it in GCP Storage Browser adding it manually in "Edit metadata" for such object (remember to refresh FB console in browser).
At this point we can think of why it's looks like this. I have found interesting information in github here.

How to get md5Checksum field of my own Google Drive files (by working with classes and objects only, without http-requests)?

I want to check md5 checksum of my uploaded files on google drive (within the same google account).
I wanted to do this using Google Apps Script (script.google.com).
I have read the documentation thoroughly but still don't understand how to get this metadata of files.
I found many examples with using webAPI (v2,v3), but it needs handling OAuth tokens, parsing http responses... why then is there Google Apps Script?
This is my working example that fetches a file id = file name from a folder named 'test', but I want to call md5Checksum attr...
function testDrive(){
var collectionFolder = DriveApp.getFoldersByName('test');
if (collectionFolder.hasNext()){
var itemFolder = collectionFolder.next();
var collectionFile = itemFolder.getFiles();
while(collectionFile.hasNext()){
var itemFile = collectionFile.next();
Logger.log(itemFile.getId(),'=',itemFile.getName());
}
}
}
P.S.: I ever did my own checksum (sha256) function that processing every fetched file blob(binary) but it has limit of ~25mb per blob.
Solution
In the documentation you can see that a File resource includes the md5Checksum in its JSON representation.
Since there is no md5Checksum wrapper for the DriveApp Class you have to use Advanced Google Services. This feature lets you use the same functionalities of the Drive API but through the Apps Script environment. In the Apps Script Editor click Resources>Advanced Google services...>Drive API (v2). The IDE will take care of authorization the first time you run a function.
Here is an example of using the Drive API advanced service to retrieve the md5Checksums of your Drive files:
let files = Drive.Files.list();
for (item of files.items) {
Logger.log(item['md5Checksum']);
}
Note: An MD5 checksum for the content of this file. This field is only populated for files with content stored in Google Drive; it is not populated for Google Docs or shortcut files.
Reference
Drive API
Advanced Google Services

Unable to get full path of file dropped in browser due to security reasons. What to do?

I'm developing a web-application with Django, which should manage and process a huge amount of user' files in local intranet. As the Django app and user files will host in the same local network, there is no necessity to upload files, it's ok to provide Django with full network path to the file via user's view.
I realised that it's impossible to get full file path from the browser due to security reasons.
There is a lot of files a user will process every day (around 150-200), so it's not ok to ask user to manually copy-paste full file path into the app. Initial design approach supposed user to drag-n-drop files from Windows Explorer to dedicated areas in the browser.
What's my options, community?
rewrite all front-end as Electron app (yikes! Just because of it!) and use Django only as REST API backend;
rewrite ducking everything as desktop app and lose all advantages Django provides (authorization, authentication, ORM, admin panel, gosh -- lots of it);
the third funny option
I feel a little stranded. Need some advice. Thanks!
I have encountered same problem while working on this.
When I think on your options
1- There is no need to re-write whole app
Create an api endpoint on server side
Create a script(program) on client that will push real paths to server
Your files should be accessible over network
Here is the script code that I used:
Tested with python Python 3.7.4 Prints realpath of all the selected files as a list.
source
import tkinter as tk
from tkinter import filedialog
import pathlib
root = tk.Tk()
root.withdraw()
root.attributes("-topmost", True)
file_path = filedialog.askopenfilenames()
# print(file_path) #debug
files = list(file_path)
print(files)
Then you need to import either a requests and generate a Json request to your server endpoint.Or just call a curl with subprocess.
2- By your definition I assume the intranet network is trusted.So is authentication necessary.And there is a question of How many users will use same file after it is processed in server.If its one then there is no need for a django app.
Well...
I've solved my problem. With much less blood than expected, which I'm happy of. :)
First, I installed Electron and one of minimal boilerplates into a new folder in my project.
In boilerplate there is a file called main.js (or index.js, depends on boilerplate), which defines Electron application window and its contents. Inside is the line which loads content to Electron BrowserWindow object:
mainWindow.loadFile(path.join(__dirname, 'index.html'));
Fortunately, BrowserWindow object does have method loadURL, which can be used to load webpage instead of local html file:
mainWindow.loadURL('http://127.0.0.1:8000');
It means that all pages rendered by Django will be shown in Electron browser, which runs node.js instead of standard browser javascript engine. Thus, this code in Django page template will work perfectly:
<h1 id="holder">DROP FILES HERE</h1>
<p id="dropped"></p>
<script>
const dropZone = document.getElementById('holder');
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
let filesList = '\n';
for (const f of e.dataTransfer.files) filesList += f.path + '\n';
document.getElementById('dropped').innerHTML = `Full file paths: ${filesList}`;
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.stopPropagation();
});
</script>

WinRT Acces denied to file

I am having this weird problem. I have an app that can create playlists with this code
var playlist = new Windows.Media.Playlists.Playlist();
...
WinJS.Utilities.id("appbar-save-button").listen("click", function ()
{
var savePicker = new Windows.Storage.Pickers.FolderPicker();
savePicker.fileTypeFilter.append("*");
savePicker.pickSingleFolderAsync().then(function (folder)
{
playlist.saveAsAsync(folder, "My Playlist", Windows.Storage.NameCollisionOption.replaceExisting, Windows.Media.Playlists.PlaylistFormat.windowsMedia);
});
})
The problem comes when I try to acces this file with this code
WinJS.Utilities.id("appbar-open-button").listen("click", function ()
{
var openPicker = Windows.Storage.Pickers.FileOpenPicker();
openPicker.fileTypeFilter.append(".wpl");
openPicker.pickSingleFileAsync().then(function (file)
{
Windows.Media.Playlists.Playlist.loadAsync(file).then(function (playlist)
{
// Print the name of the playlist.
});
});
})
On the commented line I get an exception : Cannot access the specified file or folder (⑰ᑲÕ). The item is not in a location that the application has access to (including application data folders, folders that are accessible via capabilities, and persisted items in the StorageApplicationPermissions lists). Verify that the file is not marked with system or hidden file attributes.
I have given the application Document library capabilities with File Type associations with type .wpl but still I get this exception. How can I fix it
EDIT: Adding the videos to the future acces list seems to solve the problem for app created playlists, but for random playlists the problem persists.
As far as i can see, the issue is not with the permission to load the 'playlist' file; using the file picker if a file is selected at any location on the computer, user will get access to the file; same applies to the folder picker where access to all files in the folder will be there. After that, if the selected file/folder is added to the FutureAccessList, the folder/file will be accessible later also.
The playlist may be containing files that are in the folders not accessible to user. To confirm this, try to open a playlist with no files or files in the music library location only - after giving the application 'music library' capability. If this works - application need to have settings to add folders with the music. playlist that will contain files in the selected folders will only load.

Writing a custom file format downloader in Google Spreadsheets

What I'm aiming to do is write something akin to the File > Download as > * functionality currently in Google Spreadsheets, but I want it to be in a custom format.
Specifically, I want to turn a spreadsheet of financial transactions into an QIF or OFX file for importing into accounting software. In essence, pushing a button on the UI will download a QIF/OFX version of the currently open spreadsheet.
I have tried the following so far:
Publishing a service (via implementing doGet) that uses ContentService to create the custom file and return it as a download using TextOutput.downloadAsFile(). This works if I call the endpoint directly using my browser.
Tried redirecting the browser to the Service's URL via window.location, but that doesn't seem to be available in the context of the App Script.
Tried using UrlFetchApp.fetch to have the front-end (the spreadsheet) have the browser navigate to the URL for the service. This didn't work either (not really surprising).
So, is this the right approach here? How else can I attack this?
Looks like this is what you have to do.
Create a UiApp
Add a link to the download inside that app.
Show the UiInstance on the spreadsheet
It's a little bit bulky, but it seems the be the cleanest solution.
function downloadAsWhatever() {
var app = UiApp.createApplication();
app.add(app.createAnchor("Download the file now!", "https://script.google.com/macros/s/[...]/exec"))
SpreadsheetApp.getActiveSpreadsheet().show(app.setWidth(300).setHeight(150));
}
You can set the size of the app to whatever you'd like. I chose to set it smaller so it takes up less space on the Spreadsheet screen.
For now the best way to direct a user to a new page is to create an anchor widget and have them click it. We understand that this is not an ideal user experience, but it should suffice for must use cases.
Source

Categories

Resources