Converting Excel to Gsheet and then importrange them without needing Auth - google-apps-script

I receive files in .xlsx which is updated about daily. To use the importrange function I need to have the xlsx file converted to gSheet. I've completed the conversion as per drive API
function importXLS(){
var folderBId = "redacted"; // This is the folder where we will save the gsheet converted files
fileID = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("DataFile").getValue();
var xFile = DriveApp.getFileById(fileID);
var name = xFile.getName();
if (name.indexOf('.xlsx')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = {
title : name+'_converted',
parents: [{id: folderBId}] // Added
};
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
SpreadsheetApp.getActiveSpreadsheet().getRangeByName("GDatafile").setValue(file.id); // **Notes below**
// Drive.Files.remove(ID); // Added // If this line is run, the original XLSX file is removed. So please be careful this.
}
So I save the new gSheet ID in my spreadsheet so I can use importrange to import it. But because it's a new gsheet file for every update, and not an overwritten version of the old one, I need to explicitly give access to the file. The point here is to automate, and having to click "allow" for every time you access it is not ideal.
Can I somehow (since I have the fileID at my disposal here anyway, and the spreadsheet that will want to access it) allow access right there? Or is there a way that I can set up this 'converting' code to overwrite the old gsheet (so that I only have to allow access once).

This worked for me:
function importXLS(){
var folderBId = "redacted"; // This is the folder where we will save the gsheet converted files
fileID = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("DataFile").getValue();
var xFile = DriveApp.getFileById(fileID);
var name = xFile.getName();
if (name.indexOf('.xlsx')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = {
title : name+'_gsheet',
parents: [{id: folderBId}]
};
var GDataFile = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("GDatafile").getValue();
if (GDataFile == "" ){
file = Drive.Files.insert(newFile, xBlob, {
convert: true
})
Logger.log("New File Created: " + file.id);
SpreadsheetApp.getActiveSpreadsheet().getRangeByName("GDatafile").setValue(file.id);
}
else{
file = Drive.Files.update(newFile,GDataFile,xBlob); // <--- this is the update code
Logger.log("File in theory updated... " + GDataFile);
}
// Drive.Files.remove(ID); // Added // If this line is run, the original XLSX file is removed. So please be careful this.
}
}

Related

Copying XLSX file to GSheet in Google Drive

I'm trying to copy over an XLSX sheet to an existing Gsheet, both are in the same GDrive folder. This script works when I run it as Drive.Files.insert() but that causes multiple version of the file to be created each time the script is run - so I switched it over to Drive.Files.update() like below....
function importXLS(){
var myFolder= "XXXXXXXXXX"; // Set the folder ID of "FolderB" if you want to copy over to a different folder
var files = DriveApp.getFolderById(myFolder).searchFiles('title = "XXXXX.xlsx"');
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
if (name.indexOf('.xlsx')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = {
title : name + '_converted',
parents: [{'id': myFolder}],
supportsAllDrives: true
};
file = Drive.Files.update(newFile, xBlob, { //Originally using Drive.Files.insert() but was creating multiple versions. I want to overwrite the existing version.
convert: true, supportsAllDrives: true
});
}
}
}
But I'm getting the following error when this runs...
Exception: The mediaData parameter only supports Blob types for upload.
Any idea on what I'm doing wrong? This is my first time using Google Apps Script and javascript so I'm kinda flying blind here.
I believe your goal is as follows.
You have a folder in Google Drive. There are XLSX files and Google Spreadsheets in the folder.
You want to overwrite the Google Spreadsheet with the XLSX file by searching the filename.
Filename of the XLSX file is like XXXXX.xlsx.
Filename of the Google Spreadsheet file is like XXXXX.xlsx_converted.
Modification points:
In the case of Drive.Files.update, it is required to include the file ID of the target file.
In your script, the file ID of the target Spreadsheet is not retrieved.
The 2nd argument of Drive.Files.update is required to be the file ID. But, your script uses blob. I thought that this might be the reason for your current issue.
When these points are reflected in your script, how about the following modification?
Modified script:
function importXLS() {
var myFolder = "XXXXXXXXXX"; // Please set the folder ID.
var filename = "XXXXX.xlsx"; // Please set the filename of XLSX file.
var folder = DriveApp.getFolderById(myFolder);
var files = folder.searchFiles(`title="${filename}" and mimeType="${MimeType.MICROSOFT_EXCEL}" and trashed=false`);
while (files.hasNext()) {
var xFile = files.next();
var name = xFile.getName();
if (name.indexOf('.xlsx') > -1) {
var xBlob = xFile.getBlob();
var spreadsheetFileName = name + '_converted';
var spreadsheet = folder.searchFiles(`title="${spreadsheetFileName}" and mimeType="${MimeType.GOOGLE_SHEETS}" and trashed=false`);
if (spreadsheet.hasNext()) {
Drive.Files.update(null, spreadsheet.next().getId(), xBlob, { convert: true, supportsAllDrives: true });
} else {
var newFile = { title: spreadsheetFileName, parents: [{ 'id': myFolder }] };
file = Drive.Files.insert(newFile, xBlob, { convert: true, supportsAllDrives: true });
}
}
}
}
When this script is run, the XLSX file of XXXXX.xlsx is retrieved from the specific folder. And, Google Spreadsheet of the filename of XXXXX.xlsx_converted is searched from the same folder. When the Spreadsheet is found, the Spreadsheet is overwritten by the XLSX file. When the Spreadsheet is not found, a new Spreadsheet is created by converting the XLSX file.
Reference:
Files: update

How can i remove the old converted file and replace with a new file using app script?

I am trying to replace the old converted files with the new file. This is because every time i run the script it keeps on duplicating and multiplying in the same folder.
Here is the code:
function ConvertFiles() {
var sheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var r= 2;
for(r= 2;r < sheet.getLastRow(); r++){
var fileId= sheet.getRange(r,1).getValues();
var folderID = sheet.getRange(r,8).getValues();
Logger.log(fileId);
var files = DriveApp.getFileById(fileId);
var name = files.getName().split('.')[0];
var blob = files.getBlob();
var destinationFolderId = DriveApp.getFolderById(folderID);
Logger.log(folderID);
var newFile = {
title : name + '_converted', parents: [{id:
destinationFolderId.getId()}]};
Logger.log(newFile);
}
}
My goal is:
To replace/update the old converted file into the latest one everytime the script runs (if it has the same filename)
I would like to push back the converted fileId into the google sheet to be displayed.
How can i solve this issue?
I have added comments to show each part of the code. Kindly check the whole script below:
Script:
function ConvertFiles() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var r= 2;
for(r= 2;r < sheet.getLastRow(); r++){
// Use getValue instead of getValues
var fileId = sheet.getRange(r,1).getValue();
var folderID = sheet.getRange(r,8).getValue();
var files = DriveApp.getFileById(fileId);
var name = files.getName().split('.')[0];
var blob = files.getBlob();
var newFile = {
// Remove '_converted' from name if existing to avoid duplication of the string before adding '_converted'
// This will allow to have newly converted file "replace" the old converted file properly
title: name.replace('_converted','') + '_converted',
parents: [{
id: folderID
}]
};
var destinationFolderId = DriveApp.getFolderById(folderID);
var existingFiles = destinationFolderId.getFilesByName(newFile.title);
// GOAL #1: To replace/update the old converted file into the latest one everytime the script runs (if it has the same filename)
// Find the file with same name of the file to be converted
while(existingFiles.hasNext()) {
// ID of the file with same converted name
var oldConvertedFileWithSameNameID = existingFiles.next().getId();
// Delete before writing
Drive.Files.remove(oldConvertedFileWithSameNameID);
}
// Create new converted file then get ID
var newFileID = Drive.Files.insert(newFile, blob, {
convert: true
}).id;
// Goal #2: I would like to push back the converted fileId into the google sheet to be displayed.
// Add the ID of the converted file
sheet.getRange(r,9).setValue(newFileID);
}
}
Sample sheet:
Sample files:
First run (files):
First run (sheet):
Updated original files:
Run after updating original file (files):
Run after updating original file (sheet):
Note:
This will retain the original file, but will replace the existing converted file of it everytime the script is run.
I have used Drive services.

Converting all files in drive folder from sheets to CSV and add to new folder

I have:
.xlsx files being automatically imported on a daily basis to a google drive folder
I want:
each file converted to a CSV and saved to a different folder
each file converted to a Google Sheet and saved to a different folder
the original xlsx file deleted once done processing
Currently my process is this:
Convert xlsx to 2 new files: CSV & Google Sheet
Save CSVs to CSV Folder
Save Google Sheets to Sheets folder
I was originally using this https://ctrlq.org/code/20248-convert-excel-to-csv tutorial to convert to CSV until I realized it saved an "Untitled" copy of each .xlsx sheet as Drive spreadsheet to my root folder. I could not figure out how to assign a title or folder location to those untitled. Being able to do that would also fix my immediate problem.
Then I attempted using a modified version of this (below) https://ctrlq.org/code/20500-convert-microsoft-excel-xlsx-to-google-spreadsheet with the MimeType .CSV which correctly placed my CSV's in the right folder with the right name, but the data wasn't parsed correctly and looked corrupted.
function exceltoSheets() {
var SOURCE_XLS_FOLDER = '123465';
var SHEET_FOLDER = '789456';
var CSV_TEST = '456123';
var sourceFolderID = DriveApp.getFolderById(SOURCE_XLS_FOLDER),
mimes = [MimeType.MICROSOFT_EXCEL, MimeType.MICROSOFT_EXCEL_LEGACY];
for (var m = 0; m < mimes.length; m++) {
var sourceFiles = sourceFolderID.getFilesByType(mimes[m]);
while (sourceFiles.hasNext()) {
try {
var sourceFile = sourceFiles.next();
var sourceName = sourceFile.getName().replace(".xlsx","");
var sourceNameTC = sourceName + ".csv"
var sourceNameDS = "ds_data_import_" + sourceName;
var fileId = sourceFile.getId();
var blob = sourceFile.getBlob();
var resourceDS = {
title: sourceNameDS,
mimeType: MimeType.GOOGLE_SHEETS,
convert: true,
parents: [{id: SHEET_FOLDER}]
};
var resourceTC = {
title: sourceNameTC,
mimeType: MimeType.CSV,
convert: true,
parents: [{id: CSV_TEST}],
};
Drive.Files.insert(resourceDS, blob);
Drive.Files.insert(resourceTC, blob);
} catch (f) {
Logger.log(f.toString());
}
sourceFile.setTrashed(true);
}
}
}
If I parse the CSVs correctly I end up with Untitled sheets in my root folder, if I parse the Sheets correctly I end up with corrupted CSVs. I want the result:
xlsx converted to CSV in designated folder
xlsx converted to Google Sheet in designated folder
xlsx deleted off drive once processing complete
You want to convert XLSX files in the specific folder to Google Spreadsheet.
You want to put the converted Spreadsheet to the specific folder.
You want to achieve this by modifying your script.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
In order to retrieve the files of MimeType.MICROSOFT_EXCEL and MimeType.MICROSOFT_EXCEL_LEGACY, I used searchFiles().
In order to convert from XLSX file to Google Spreadsheet and put it to the specific folder, I used Drive.Files.copy().
Modified script:
When you use this, please confirm whether Drive API is enabled at Advanced Google services.
function exceltoSheets() {
var SOURCE_XLS_FOLDER = '###'; // Please set the source folder ID here.
var dstFolderId = '####'; // Please set the destination folder ID here.
var sourceFolderID = DriveApp.getFolderById(SOURCE_XLS_FOLDER);
var searchQuery = "mimeType='" + MimeType.MICROSOFT_EXCEL + "' or mimeType='" + MimeType.MICROSOFT_EXCEL_LEGACY + "'";
var sourceFiles = sourceFolderID.searchFiles(searchQuery);
while (sourceFiles.hasNext()) {
var sourceFile = sourceFiles.next();
var fileId = sourceFile.getId();
Drive.Files.copy({mimeType: MimeType.GOOGLE_SHEETS, parents: [{id: dstFolderId}]}, fileId);
sourceFile.setTrashed(true);
}
}
Note:
If you want to directly delete the XLSX file, you can use Drive.Files.remove(fileId) instead of sourceFile.setTrashed(true).
References:
searchFiles()
Files: copy
Advanced Google services
Edit:
You want to convert from XLSX files to CSV and Google Spreadsheet files.
You want to put the converted XLSX files and CSV files to each folder.
For this situation, the modified script is as follows.
function exceltoSheets() {
var SOURCE_XLS_FOLDER = '###'; // Please set the source folder ID here.
var dstFolderIdForSpreadsheet = '###'; // Please set the destination folder ID for Spreadsheet here.
var dstFolderIdForCSV = '###'; // Please set the destination folder ID for CSV here.
var sourceFolder = DriveApp.getFolderById(SOURCE_XLS_FOLDER);
var destinationFolderForCSV = DriveApp.getFolderById(dstFolderIdForCSV);
var searchQuery = "mimeType='" + MimeType.MICROSOFT_EXCEL + "' or mimeType='" + MimeType.MICROSOFT_EXCEL_LEGACY + "'";
var sourceFiles = sourceFolder.searchFiles(searchQuery);
while (sourceFiles.hasNext()) {
var sourceFile = sourceFiles.next();
var fileId = sourceFile.getId();
var spreadsheet = Drive.Files.copy({mimeType: MimeType.GOOGLE_SHEETS, parents: [{id: dstFolderIdForSpreadsheet}]}, fileId);
var sheets = SpreadsheetApp.openById(spreadsheet.id).getSheets();
sheets[0].getDataRange().getValues()
var csv = sheets.map(function(sheet) {return sheet.getDataRange().getValues().reduce(function(csv, row) {return csv += row.join(",") + "\n"}, "")});
destinationFolderForCSV.createFile(spreadsheet.title + ".csv", csv, MimeType.CSV)
sourceFile.setTrashed(true);
}
}

Drive.Files.Update - Uploading a new revision to maintain doc ID error

I'm trying to upload a new revision to an existing file (original file) and maintain the ID for that file.
I have the Google Drive API (v2) enabled in the Advanced Google Services. I have the script as a standalone script (not attached to a google doc or spreadsheet based on something I read that said the Drive.Files.Update doesn't work for those scripts).
On this line of code:
Drive.Files.update({
title: currentFile.getName(), mimeType: currentFile.getMimeType()
}, originalFileID, blobOfNewContent);
I get the following error:
Execution failed: We're sorry, a server error occurred. Please wait a bit and try again. (line 13, file "Code") [8.215 seconds total runtime]
Any clues as to what else I can try? Basically, I need to maintain the ID of the file and just update the contents every so often from other file.
function overwriteFile(blobOfNewContent, originalFileID)
{
var currentFile;
currentFile = DriveApp.getFileById(originalFileID);
if (originalFileID != "")
{//If original File exsts
Drive.Files.update({
title: currentFile.getName(), mimeType: currentFile.getMimeType()
}, originalFileID, blobOfNewContent);
}
}
function getNewestSlidesInFolderAndUploadToStaticFileID()
{
// fileID of file that is "Original File"
var mainfileID = "some id";
var folders = DriveApp.getFoldersByName('Testing Slides');
var arryFileDates = [];
var objFilesByDate = {};
//find most recent file
while (folders.hasNext())
{
var folder = folders.next();
var files = folder.getFilesByType("application/vnd.google-apps.presentation");
while (files.hasNext())
{
var file = files.next();
// Make sure the fileID is not the static file
if (file.getId() != mainfileID)
{
var fileDate = file.getLastUpdated();
objFilesByDate[fileDate] = file.getId(); //Create an object of file names by file ID
arryFileDates.push(file.getLastUpdated());
} // end if id = mainfileID
} //end while (files.hasNext())
arryFileDates.sort(function(a, b) { return b - a});
var newestDate = arryFileDates[0];
Logger.log('Newest date is: ' + newestDate);
var newestFileID = objFilesByDate[newestDate];
Logger.log('newestFile: ' + newestFileID);
//return newestFile;
}//while folders.hasNext()
//Get the contents of the most recent file
var newestFile = DriveApp.getFileById(newestFileID);
var blob = newestFile.getBlob();
overwriteFile(blob, mainfileID);
//delete most recent file so that only the main file exists
newestFile.setTrashed(true);
}
I did find all this code here on stackoverflow and I just modified (hardcoded) some pieces for my needs. Thanks to the original coders for their assistance.

Skip processing .xls to Google Sheets script if the file already exists in Google Drive

I am currently using this code to automatically convert all uploaded .xls files in Google Drive to Google Sheets.
function importXLS(){
var files = DriveApp.searchFiles('title contains ".xls"');
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
if (name.indexOf('.xls')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name,
key : ID,
'parents':[{"id":"12FcKokB-ppW7rSBtAIG96uoBOJtTlNDT"}]
}
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
}
}
}
It works perfectly, but fails if there is already a file in the output folder with the same name. Even though I never technically get to see this error below (since it runs on a schedule and not fired manually like in the screenshot), I would prefer to simply skip the conversion process if the file already exists.
If possible, I would also like to avoid overwriting it each time, as I feel that would be a waste of processing time. How would I edit this code to say that if the file name already exists in that folder, skip the entire code completely?
Thanks!
Two things you can try:
Get the files names that are already in the destination folder and check if the file exists before you try copying.
Wrap the section of your code that does the copying in a try..catch statement.
Both of these should work independently, but using the try..catch statement will catch all errors, so it would be best to combine them. (You can review the error logs in the Developer Console.) Doing this you'll be able to skip files that have the same name as those already in your destination folder and any other error that might come up will not terminate your script from completing.
function importXLS(){
var files = DriveApp.searchFiles('title contains ".xls"');
var destinationFolderId = "12FcKokB-ppW7rSBtAIG96uoBOJtTlNDT";
var existingFileNames = getFilesInFolder(destinationFolderId);
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
try {
if (!existingFileNames[name] && (name.indexOf('.xls')>-1)) {
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name,
key : ID,
'parents':[{"id": destinationFolderId}]
}
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
}
} catch (error) {
console.error("Error with file " + name + ": " + error);
}
}
}
/**
* Get an object of all file names in the specified folder.
* #param {string} folderId
* #returns {Object} files - {filename: true}
*/
function getFilesInFolder(folderId) {
var folder = DriveApp.getFolderById(folderId);
var filesIterator = folder.getFiles();
var files = {};
while (filesIterator.hasNext()) {
var file = filesIterator.next();
files[file.getName()] = true;
}
return files;
}