Google Apps Script Unzipping Files - Issues - google-apps-script

I have a script that looks in a destination folder and unzips files to a new folder. All works perfect as long as the zip contains just one file; anymore and I only seem to unzip the first file.
Im pretty sure the line that's causing this is which is picking the first record in the array of files however I am not quite skilful enough to figure out how to get around this. Any help would be greatly appreciated
[var newDriveFile = DestinationFolder.createFile(unZippedfile[0]);]
//Unzips all ZIP files in the folder
function Unzip() {
//Add folder ID to select the folder where zipped files are placed
var SourceFolder = DriveApp.getFolderById("14vRNV3FZJicplxeurycsfBHmETrUQBex")
//Add folder ID to save the where unzipped files to be placed
var DestinationFolder = DriveApp.getFolderById("1OJXkPrUqcqapsv366oCV_ZsvaECstKTn")
//Select the Zip files from source folder using the Mimetype of ZIP
var ZIPFiles = SourceFolder.getFilesByType(MimeType.ZIP)
//var ZIPFiles = SourceFolder.getFilesByName('MTTR.zip')
//Loop over all the Zip files
while (ZIPFiles.hasNext()){
// Get the blob of all the zip files one by one
var fileBlob = ZIPFiles.next().getBlob();
//Use the Utilities Class to unzip the blob
var unZippedfile = Utilities.unzip(fileBlob);
//Unzip the file and save it on destination folder
var newDriveFile = DestinationFolder.createFile(unZippedfile[0]); //I think this line is where my issue is
}
Logger.log("Finished Unzipping files")
}

As what Rodrigo mentioned in the comments, you need to loop it on all the files. unZippedfile.length is the number of files in the zip file. Use a loop around the createFile method.
Code:
// loop to all zip files
while (ZIPFiles.hasNext()){
var fileBlob = ZIPFiles.next().getBlob();
var unZippedfile = Utilities.unzip(fileBlob);
// loop to all items inside the current zip file
for (i=0; i<unZippedfile.length; i++) {
var newDriveFile = DestinationFolder.createFile(unZippedfile[i]);
}
}
Output:
Note:
Sample output above show the unzipped files on the same directory as I instantiated both source and destination with the same directory ID.

Related

Zip all files in a folder and download the zipped file

I want to do the following:
a) zip all files inside a folder which is defined by its folder Id and return the FileId of the zipped archive
b) Create a download link from the zip's File Id so i can embed the link into my Html template so i can let users (with the right permissions to the shared drive where the .zip file is stored) download it by clicking on the link.
Now i am stuck with a)
My code so far is derived from this post: Creating a zip file inside Google Drive with Apps Script
But it seems like some of the solutions there are deprecated (f.ex. DocsList).
As i could not find up-to-date solutions from the recent 2 years that work for me i thought that it is worth to look now into the zipping case.
/*****
*** This Function returns all blobs from a folder
*/
function getBlobs(folderId) {
let blobs = [];
folder = DriveApp.getFolderById(folderId)
let files = folder.getFiles();
while (files.hasNext()) {
let file = files.next().getBlob();
blobs.push(file);
return blobs;
}
}
/*****
*** This Function zips all blobs in a folder and returns the zip file ID
*/
function zipFilesInFolder(blobs, filename) {
let zip = Utilities.zip(blobs, filename + '.zip');
zipFileId = zip.getId()
Logger.log("{zipFilesInFolder} [zipFileId]: " + zipFileId)
return zipFileId;
}
filename = "myzip"
folderId = "123456789a94..."
blobs = getBlobs(folderId)
zipFileId = zipFilesInFolder(blobs, filename)
THANK YOU Tanaike!
i fixed my code after following your kind suggestions:
/*****
*** This Function returns all blobs from a folder
*/
function getBlobs(folderId) {
let blobs = [];
// Get the folder containing the files to be zipped by its folderId
folder = DriveApp.getFolderById(folderId)
// Get the files from that folder
let files = folder.getFiles();
// Iterate through the files and add each ones to the array of blobs
while (files.hasNext()) {
let file = files.next().getBlob();
blobs.push(file);
}
return blobs;
}
/*****
*** This Function zips all blobs in a folder and returns the zip file ID
*/
function zipFilesInFolder(folderId, blobs, filename) {
// Get the folder containing the files to be zipped by its folderId
folder = DriveApp.getFolderById(folderId)
let zipped = Utilities.zip(blobs, filename + '.zip');
// Create the .zip Archive File and in the parent folder
// of the folder containing the files which were zipped
// and return its FileId
zippedFile = folder.getParents().next().createFile(zipped);
zipFileId = zippedFile.getId();
Logger.log("{zipFilesInFolder} [zipFileId]: " + zipFileId)
return zipFileId;
}
And now the a) part works like a charm! Thank you for your fast reply!

Is there any way I can convert CSV files w/o avoid converting already converted files

Hi im absolute novice with google script in general, ive been tweaking around with this script i found from Bulk convert csv files in Google drive to Google Sheets:
function convert() {
var folder = DriveApp.getFolderById('folder id here');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
Drive.Files.copy({}, file.getId(), {convert: true});
}
}
and it works like a charm. the problem is that the this code indiscriminately convert every file in the said folder, including the converted file and source file. so if i run the script for the 2nd i would end up with a new copy of the converted file and another copy of the source file.
is there any way to work around this? i was thinking something along the lines of moving the converted files into a different folder and allow file overwriting to avoid copies.
thank you so much in advance.
How about this modification?
In this modification, I would like to propose the following workaround.
Workaround:
In this workaround, the mimeType of the source file is checked, and also, the description is given the copied source file. The description is used as the checker whether the file had been copied. By this, when the script is run for the 1st time, the CSV source files are converted to Google Spreadsheet. At that time, the description is added to the source files as copied. And, in the 2nd run, the files with the mimeType of CSV and the description has no value of copied are copied and converted. By this flow, the files which has been copied are not used after 2nd run.
When above workaround is reflected to your script, it becomes as follows.
Sample script:
function convert() {
var folder = DriveApp.getFolderById('folder id here');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getMimeType() == MimeType.CSV && file.getDescription() != "copied") { // Added
file.setDescription("copied"); // Added
Drive.Files.copy({}, file.getId(), {convert: true});
}
}
}
Note:
When you have already used the description of the file, you can also use the property of the file.
If you want to copy the source CSV files every run, you can also use the following modified script. In this case, only the mimeType is checked.
function convert() {
var folder = DriveApp.getFolderById('folder id here');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getMimeType() == MimeType.CSV) { // Added
Drive.Files.copy({}, file.getId(), {convert: true});
}
}
}
References:
getMimeType()
getDescription()
setDescription(description)

Unzip File Only If New in Google Apps Scripts

I have a folder (let's call it the source folder) on Google Drive that is updated from time to time with new zip files (underlying files are PDFs). I am trying to use Google Apps Script to unzip only the new zip files and place the underlying PDFs in another folder (let's call it the destination folder).
I am currently using the following code to unzip the files in the source folder, running on a time-based trigger. My current code does not differentiate between old and new zip files so I am getting a large number of duplicates accumulating in the destination folder. (I found this code on WeirdGeek: https://www.weirdgeek.com/2019/10/unzip-files-using-google-apps-script/)
function Unzip() {
//Add folder ID to select the folder where zipped files are placed
var SourceFolder = DriveApp.getFolderById("1KbyB2vTUfbwYdzBEyIwzTliXKjATbW8A")
//Add folder ID to save the where unzipped files to be placed
var DestinationFolder = DriveApp.getFolderById("1Z-iVlcROe5kVX8IkBlV9a98WKlvlfp3U")
//Select the Zip files from source folder using the Mimetype of ZIP
var ZIPFiles = SourceFolder.getFilesByType(MimeType.ZIP)
//Loop over all the Zip files
while (ZIPFiles.hasNext()){
// Get the blob of all the zip files one by one
var fileBlob = ZIPFiles.next().getBlob();
//Use the Utilities Class to unzip the blob
var unZippedfile = Utilities.unzip(fileBlob);
//Unzip the file and save it on destination folder
var newDriveFile = DestinationFolder.createFile(unZippedfile[0]);
}
}
I initially thought to add some sort of time-based restriction to the function, but because the source folder is being synced (using MultCloud) with an sFTP site, I don't want to go that direction.
I ALSO found the following code is used to put a "replace" restriction on saving new spreadsheets but couldn't figure out how to integrate this with my code. (Code is from user Tainake)
function saveAsSpreadsheet() {
var folderId = "0B8xnkPYxGFbUMktOWm14TVA3Yjg";
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFilesByName(getFilename());
if (files.hasNext()) {
files.next().setTrashed(true);
}
var sheet = SpreadsheetApp.getActiveSpreadsheet();
DriveApp.getFileById(sheet.getId()).makeCopy(getFilename(), folder);
}
Any ideas on how to solve this problem would be appreciated! I am a complete noob so I apologize in advance if this is a stupid question.
EDIT: I could not figure out how to unzip only "new" files in the source folder, and so my new code moves to trash all files in the destination folder, and then unzips all files in the source folder. Code is below:
function Unzip() {
//Add folder ID to select the folder where zipped files are placed
var SourceFolder = DriveApp.getFolderById("1KbyB2vTUfbwYdzBEyIwzTliXKjATbW8A")
//Add folder ID to save the where unzipped files to be placed
var DestinationFolder = DriveApp.getFolderById("1Z-iVlcROe5kVX8IkBlV9a98WKlvlfp3U")
//Delete files from the destination folder
//Get the files in the destination folder
var files = DestinationFolder.getFiles();
//Loop through the files in the destination folder
while(files.hasNext()){
//Get the individual file in the destination folder to process
var file = files.next();
//Trash that file
file.setTrashed(true);
}
//Select the Zip files from source folder using the Mimetype of ZIP
var ZIPFiles = SourceFolder.getFilesByType(MimeType.ZIP)
//Loop over all the Zip files
while (ZIPFiles.hasNext()){
// Get the blob of all the zip files one by one
var fileBlob = ZIPFiles.next().getBlob();
//Use the Utilities Class to unzip the blob
var unZippedfile = Utilities.unzip(fileBlob);
//Unzip the file and save it on destination folder
var newDriveFile = DestinationFolder.createFile(unZippedfile[0]);
}
}
I could see how this may not be the best solution to this issue, but this allows me to have a MultCloud sync the zip files into my Google Drive, and then allows me to have those files unzipped with a function that runs from time to time. Anyone have a better idea how to accomplish the same thing without deleteing and recreating all the files every time?
EDIT 2:
Thank you to Cameron, this question is answered. I am pasting the full code I am using below, for posterity / other newbies so that they don't have to piece it together:
function Unzip() {
//Add folder ID to select the folder where zipped files are placed
var SourceFolder = DriveApp.getFolderById("1KbyB2vTUfbwYdzBEyIwzTliXKjATbW8A")
//Add folder ID to save the where unzipped files to be placed
var DestinationFolder = DriveApp.getFolderById("1Z-iVlcROe5kVX8IkBlV9a98WKlvlfp3U")
//Select the Zip files from source folder using the Mimetype of ZIP
var ZIPFiles = SourceFolder.getFilesByType(MimeType.ZIP);
var now = new Date(); //get current time after you fetch the file list from Drive.
//Get script properties and check for stored "last_execution_time"
var properties = PropertiesService.getScriptProperties();
var cutoff_datetime = properties.getProperty('last_execution_time');
//if we have last execution date, stored as a string, convert it to a Date object.
if(cutoff_datetime)
cutoff_datetime = new Date(cutoff_datetime);
//Loop over all the Zip files
while (ZIPFiles.hasNext()){
var file = ZIPFiles.next();
//if no stored last execution, or file is newer than last execution, process the file.
if(!cutoff_datetime || file.getDateCreated() > cutoff_datetime){
var fileBlob = file.getBlob();
//Use the Utilities Class to unzip the blob
var unZippedfile = Utilities.unzip(fileBlob);
//Unzip the file and save it on destination folder
var newDriveFile = DestinationFolder.createFile(unZippedfile[0]);
}
}
//store "now" as last execution time as a string, to be referenced on next run.
properties.setProperty('last_execution_time',now.toString());
}
You can use the getDateCreated() function on a File object to determine when the file was created. By checking this value against a time limit, you should be able to determine if the file is new. If you are triggering your script with at least a few hours between executions, you could use a hardcoded cutoff time. So if you are triggering your script every six hours, you could ignore any files not created within the last 6 hours, for example.
However, a more robust approach would be to store the last successful execution time in a Script Property, so you could always process any files created since the last successful execution.
Note that this code will process all files currently in the folder the first time it runs, after that it will only process files created since the last run.
var ZIPFiles = SourceFolder.getFilesByType(MimeType.ZIP);
var now = new Date(); //get current time after you fetch the file list from Drive.
//Get script properties and check for stored "last_execution_time"
var properties = PropertiesService.getScriptProperties();
var cutoff_datetime = properties.getProperty('last_execution_time');
//if we have last execution date, stored as a string, convert it to a Date object.
if(cutoff_datetime)
cutoff_datetime = new Date(cutoff_datetime);
//Loop over all the Zip files
while (ZIPFiles.hasNext()){
var file = ZIPFiles.next();
//if no stored last execution, or file is newer than last execution, process the file.
if(!cutoff_datetime || file.getDateCreated() > cutoff_datetime){
var fileBlob = file.getBlob();
//Use the Utilities Class to unzip the blob
var unZippedfile = Utilities.unzip(fileBlob);
//Unzip the file and save it on destination folder
var newDriveFile = DestinationFolder.createFile(unZippedfile[0]);
}
}
//store "now" as last execution time as a string, to be referenced on next run.
properties.setProperty('last_execution_time',now.toString());

Do not make copy of existing file in destination folder

I've been trying to copy contents from one folder to another folder in google drive using google app script. With some online help and documentation of google script, I was able to copy files from one folder to other but it copies all files from source folder to destination folder on time trigger, but I want only the new files to get copied in my destination folder and skip the present files that are already same in my source and destination folder.
I tried to check files by name before copying but couldn't find a way to compare files by name in two different folders of gdrive.
function CopyFiles() {
var SourceFolder = DriveApp.getFolderById('Sid');
var SourceFiles = DriveApp.getFolderById('Sid').getFiles();
var DestFolder = DriveApp.getFolderById('Did');
var DestFiles = DriveApp.getFolderById('Did').getFiles();
while (SourceFiles.hasNext())
{
var files = SourceFiles.next();
var dfiles = DestFiles.next();
if ( files == dfiles){
file.setTrashed(true);}
else{
var f = files.makeCopy(DestFolder);
}
}
}
What I want to achieve is that Script compares files by name in destination folder and if files by that name already exist than skip else, create a copy of that new file in the destination folder.
Copies files from source to destination if destination does have the same file names
In this version no files get trashed.
function CopyFiles() {
var srcFldr=DriveApp.getFolderById('srcId');
var srcFiles=srcFldr.getFiles();
var desFldr=DriveApp.getFolderById('desId');
var desFiles=desFldr.getFiles();
var dfnA=[];
while(desFiles.hasNext()) {
var df=desFiles.next();
dfnA.push(df.getName());
}
while(srcFiles.hasNext()) {
var sf=srcFiles.next();
if(dfnA.indexOf(sf.getName())==-1) {
sf.makeCopy(sf.getName(),desFldr);
}
}
}

Download Folder as Zip Google Drive API

I currently have a Google App Script in Google Sheet that gives me the URL of a folder, which I can then use to download. Though it is an extra step I would like to remove, and get the URL of the zipped content directly.
Here's my code (google app script):
function downloadSelectedScripts() {
// ...
var scriptFiles = getScriptFiles(scriptFileNames)
var tempFolder = copyFilesToTempFolder(scriptFiles)
Browser.msgBox(tempFolder.getUrl())
}
function copyFilesToTempFolder(files) {
var tempFolder = DriveApp.getFolderById(FOLDERS.TEMP)
var tempSubFolder = tempFolder.createFolder('download_' + Date.now())
for (var i in files) {
var file = files[i]
file.makeCopy(file.getName(), tempSubFolder)
}
return tempSubFolder
}
You want to compress all files in a folder as a zip file.
The folder has no subfolders.
All files are only text files.
The total size of all files is less than 50 MB.
You want to retrieve the URL for downloading the zip file.
If my understanding is correct, how about this sample script? The flow of this script is as follows.
Retrieve folder.
Retrieve blobs of all files in the folder.
Compress blobs and retrieve a blob of zip.
Create a zip blob as a file.
Retrieve URL for downloading.
Sample script:
When you use this script, please set the folder ID of folder that you want to compress.
function myFunction() {
var folderId = "###"; // Please set the folder ID here.
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFiles();
var blobs = [];
while (files.hasNext()) {
blobs.push(files.next().getBlob());
}
var zipBlob = Utilities.zip(blobs, folder.getName() + ".zip");
var fileId = DriveApp.createFile(zipBlob).getId();
var url = "https://drive.google.com/uc?export=download&id=" + fileId;
Logger.log(url);
}
Result:
The direct link of the zip file is returned as follows.
https://drive.google.com/uc?export=download&id=###
Note:
In the current stage, unfortunately, the folders cannot be included in the zip data with Google Apps Script.
In this sample script, the filename of zip file is the folder name. So please modify it for your situation.
If you want to download the zip file without login to Google, please share the file.
Reference:
zip(blobs, name)