Can I generate a file from Google Sheets script? - google-apps-script

I'm using Google Sheets to prototype a bunch of numerical data for something I'm doing.
Is there a way to export a subset to a text file?
Effectively, what I'm aiming to do is export a file I can include directly in the build for another project.
So is there a way to generate a text file for download?

If you have a Google Apps account, then you can use DocsList.createFile() to create the text file and save it in your documents list.
Section 3 of this tutorial shows how to save the selected range of a spreadsheet as a file in your documents list in CSV format. It could be modified pretty easily to save in a different format.

I have the texts of my project in some columns of a Google Spreadsheet.
I took this script tutorial from Google and modified it to select only a specific range (in the example below it's D4:D).
It generates a CSV file in your Drive root folder. It still doesn't download the file - I'm working on that now.
Hope it helps!
/* The code below is a modification from this tutorial: https://developers.google.com/apps-script/articles/docslist_tutorial#section3 */
/* The code below is a modification from this tutorial: https://developers.google.com/apps-script/articles/docslist_tutorial#section3 */
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{name: "Save as CSV file", functionName: "saveAsCSV"}];
ss.addMenu("CSV", csvMenuEntries);
}
function saveAsCSV() {
// Name the file
fileName = "quests.csv";
// Convert the range data to CSV format
var csvFile = convertRangeToCsvFile_(fileName);
// Create a file in the root of my Drive with the given name and the CSV data
DriveApp.createFile(fileName, csvFile);
}
function convertRangeToCsvFile_(csvFileName) {
// Get from the spreadsheet the range to be exported
var rangeToExport = SpreadsheetApp.getActiveSpreadsheet().getRange("D4:D");
try {
var dataToExport = rangeToExport.getValues();
var csvFile = undefined;
// Loop through the data in the range and build a string with the CSV data
if (dataToExport.length > 1) {
var csv = "";
for (var row = 0; row < dataToExport.length; row++) {
for (var col = 0; col < dataToExport[row].length; col++) {
if (dataToExport[row][col].toString().indexOf(",") != -1) {
//dataToExport[row][col] = "\"" + dataToExport[row][col] + "\"";
dataToExport[row][col] = dataToExport[row][col];
}
}
// Join each row's columns
// Add a carriage return to end of each row, except for the last one
if (row < dataToExport.length-1) {
csv += dataToExport[row].join(",") + "\r\n";
}
else {
csv += dataToExport[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}

Related

Exporting big google sheets file as multiple csv with batch size 500

I would like to download a 10000 row long singular sheet from Google Sheets split into 20 separate csv files with batch size of 500 rows each. Could this be possibly done using Google Sheets?
I wasn't able to find a native support for sheets to download and separate the files into multiple parts/fragments. But I was able to find a Google Apps Script code that saves the sheet into a csv.
So what I did was modify the code and then make it turn a sheet into multiple csv's.
The catch is, it will upload all those files into a folder in the logged in google drive and then you can download them in a zip file. (I'll show this later below)
Here is the source code.
And here is the modified code I did:
function saveAsCSV() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Update numFiles into how many files you want is outputted
// The script will calculate how many rows it can place in a file equally
// Excess will be placed into an additional file
var numFiles = 5;
var rowsPerFile = Math.floor(sheet.getDataRange().getLastRow() / numFiles);
// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
// append ".csv" extension to the sheet name
fileName = sheet.getName();
// convert all available sheet data to csv format
var csvFiles = convertRangeToCsvFile_(sheet, rowsPerFile);
// create a file in the Docs List with the given name and the csv data
csvFiles.forEach(function(csvFile, i){
var csvName = fileName + '_' + (i + 1) + '.csv';
folder.createFile(csvName, csvFile);
});
}
function convertRangeToCsvFile_(sheet, rowsPerFile) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFiles = [];
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
if ((row + 1) % rowsPerFile === 0) {
csvFiles.push(csv);
csv = "";
}
}
}
// If there are remainders that doesnt reach the number of files needed.
// It will add another file, containing all remainder rows there.
// 10010 rows / 20 files = 10 rows excess
// 21st file will contain those 10 rows excess
// No excess means no 21st file
if(csv) {
csvFiles.push(csv);
}
return csvFiles;
}
catch(err) {
Logger.log(err);
}
}
Sample Data:
Sample Output:
Upon right clicking the folder created in Google Drive, you will have the option to download the whole folder as zip file containing all the csv files.
Here is the downloaded zip file containing all csv files:
Since there are 22 rows and the sample code wants 5 files, a 6th file was created containing the excess 2 rows

How to extract files from .tar archive with Google Apps Script

Good day all,
I'm trying to get a tar.gz attachment from Gmail, extract the file and save it to Google Drive. It's a daily auto generated report which I'm getting, compressed due to >25mb raw size.
I got this so far:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setup");
var gmailLabels = sheet.getRange("B2:B2").getValue(); //I have my Gmail Label stored here
var driveFolder = sheet.getRange("B5:B5").getValue(); //I have my GDrive folder name stored here
// apply label filter, search only last 24hrs mail
var filter = "has:attachment label:" + gmailLabels + " after:" + Utilities.formatDate(new Date(new Date().getTime()-1*(24*60*60*1000)), "GMT", "yyyy/MM/dd");
var threads = GmailApp.search(filter, 0, 1); // check only 1 email at a time
var folder = DriveApp.getFoldersByName(driveFolder);
if (folder.hasNext()) {
folder = folder.next();
} else {
folder = DriveApp.createFolder(driveFolder);
}
var message = threads[0].getMessages()[0];
var desc = message.getSubject() + " #" + message.getId();
var att = message.getAttachments();
for (var z=0; z<att.length; z++) {
var attName = att[z].getName()
var attExt = attName.search('csv')
if (attExt > 0){ var fileType = "csv"; }
else {
var attExt = attName.search('tar.gz');
if (attExt > 0){ var fileType = "gzip"; }
else {
threads[x].addLabel(skipLabel);
continue;
}
}
// save the file to GDrive
try {
file = folder.createFile(att[z]);
file.setDescription(desc);
}
catch (e) {
Logger.log(e.toString());
}
// extract if gzip
if (fileType == 'gzip' ){
var ungzippedFile = Utilities.ungzip(file);
try {
gz_file = folder.createFile(ungzippedFile);
gz_file.setDescription(desc);
}
catch (e) {
Logger.log(e.toString());
}
}
}
Everything works fine, but in the last step it only decompresses the .gz file saving .tar file in the Drive. What can I do with it next? The .tar file contains a .csv file which I need to extract and process afterwards.
I should probably add that I'm limited to use GAS only.
Any help warmly appreciated.
How about this answer? Unfortunately, in the current stage, there are no methods for extracting files from a tar file in Google Apps Script, yet. But fortunately, from wiki of tar, we can retrieve the structure of the tar data. I implemented this method with Google Apps Script using this structure data.
1. Unarchive of tar data:
Before you run this script, please set the file ID of tar file to run(). Then, run run().
Sample script:
function tarUnarchiver(blob) {
var mimeType = blob.getContentType();
if (!mimeType || !~mimeType.indexOf("application/x-tar")) {
throw new Error("Inputted blob is not mimeType of tar. mimeType of inputted blob is " + mimeType);
}
var baseChunkSize = 512;
var byte = blob.getBytes();
var res = [];
do {
var headers = [];
do {
var chunk = byte.splice(0, baseChunkSize);
var headerStruct = {
filePath: function(b) {
var r = [];
for (var i = b.length - 1; i >= 0; i--) {
if (b[i] != 0) {
r = b.slice(0, i + 1);
break;
}
}
return r;
}(chunk.slice(0, 100)),
fileSize: chunk.slice(124, 124 + 11),
fileType: Utilities.newBlob(chunk.slice(156, 156 + 1)).getDataAsString(),
};
Object.keys(headerStruct).forEach(function(e) {
var t = Utilities.newBlob(headerStruct[e]).getDataAsString();
if (e == "fileSize") t = parseInt(t, 8);
headerStruct[e] = t;
});
headers.push(headerStruct);
} while (headerStruct.fileType == "5");
var lastHeader = headers[headers.length - 1];
var filePath = lastHeader.filePath.split("/");
var blob = Utilities.newBlob(byte.splice(0, lastHeader.fileSize)).setName(filePath[filePath.length - 1]).setContentTypeFromExtension();
byte.splice(0, Math.ceil(lastHeader.fileSize / baseChunkSize) * baseChunkSize - lastHeader.fileSize);
res.push({fileInf: lastHeader, file: blob});
} while (byte[0] != 0);
return res;
}
// Following function is a sample script for using tarUnarchiver().
// Please modify this to your situation.
function run() {
// When you want to extract the files from .tar.gz file, please use the following script.
var id = "### file ID of .tar.gz file ###";
var gz = DriveApp.getFileById(id).getBlob().setContentTypeFromExtension();
var blob = Utilities.ungzip(gz).setContentTypeFromExtension();
// When you want to extract the files from .tar file, please use the following script.
var id = "### file ID of .tar file ###";
var blob = DriveApp.getFileById(id).getBlob().setContentType("application/x-tar");
// Extract files from a tar data.
var res = tarUnarchiver(blob);
// If you want to create the extracted files to Google Drive, please use the following script.
res.forEach(function(e) {
DriveApp.createFile(e.file);
});
// You can see the file information by below script.
Logger.log(res);
}
2. Modification of your script:
If this script is used for your script, for example, how about this? tarUnarchiver() of above script is used. But I'm not sure how you want to use this script. So please think of this as a sample.
Sample script:
// extract if gzip
if (fileType == 'gzip' ){
var ungzippedFile = Utilities.ungzip(file);
try {
var blob = ungzippedFile.setContentType("application/x-tar"); // Added
tarUnarchiver(blob).forEach(function(e) {folder.createFile(e.file)}); // Added
}
catch (e) {
Logger.log(e.toString());
}
}
In this modified script, the blob of ungzippedFile (tar data) is put to my script and run tarUnarchiver(). Then, each file is created to the folder.
Note:
When you run this script, if an error related to mimeType occurs, please set the mimeType of "tar" to the input blob.
As the method for setting the mimeType, you can use as follows.
blob.setContentTypeFromExtension() Ref
blob.setContentType("application/x-tar") Ref
It might have already been got the mimeType in the blob. At that time, setContentTypeFromExtension() and setContentType() are not required.
If you want to retrieve the file path of each file, please check the response from tarUnarchiver(). You can see it as a property of fileInf from the response.
Limitations:
When this script is used, there is the limitations as follows. These limitations are due to Google's specification.
About the file size, when the size of tar data is over 50 MB (52,428,800 bytes), an error related to size limitation occurs.
When the size of extracted file is over 50 MB, an error occurs.
When a single file size of extracted file is near to 50 MB, there is a case that an error occurs.
In my environment, I could confirm that the size of 49 MB can be extracted. But in the case of just 50 MB
, an error occurred.
Reference:
tar (wiki)
In my environment, I could confirm that the script worked. But if this script didn't work, I apologize. At that time, can you provide a sample tar file? I would like to check it and modify the script.

How to export to CSV from spreadsheet to drive or download folder

I have tried with the below script is not working. When I download into CSV format it's all in one column instead standard format. Using Google Apps script, how to download to CSV format and file save in my drive or download?
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{name: "export as csv files", functionName: "saveAsCSV"}];
ss.addMenu("csv", csvMenuEntries);
};
How about this script? This script exports a CSV file from spreadsheet and make an user download it.
Please put both HTML and script in a GAS project.
html :
This file name is "download.html".
<!DOCTYPE html>
<html>
<body>
Download CSV?
<form>
<input type="button" value="ok" onclick="google.script.run
.withSuccessHandler(executeDownload)
.saveAsCSV();" />
</form>
</body>
<script>
function executeDownload(url) {
window.location.href = url;
}
</script>
</html>
Script :
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('csv')
.addItem('export as csv files', 'dialog')
.addToUi();
}
function dialog() {
var html = HtmlService.createHtmlOutputFromFile('download');
SpreadsheetApp.getUi().showModalDialog(html, 'CSV download dialog');
}
function saveAsCSV() {
var filename = "#####"; // CSV file name
var folder = "#####"; // Folder ID
var csv = "";
var v = SpreadsheetApp
.getActiveSpreadsheet()
.getActiveSheet()
.getDataRange()
.getValues();
v.forEach(function(e) {
csv += e.join(",") + "\n";
});
var url = DriveApp.getFolderById(folder)
.createFile(filename, csv, MimeType.CSV)
.getDownloadUrl()
.replace("?e=download&gd=true","");
return url;
}
Process :
Using "onOpen()", it addes menu for launching a dialog.
After launching the dialog, "saveAsCSV()" is launched by pushing a button. "saveAsCSV()" exports a CSV file and outputs download URL. At current script, all of data on active sheet is retrieved and exported CSV. If you want to retrieve a range you need, please use "getRange()".
The CSV file is downloaded by "executeDownload()".
You can set csv file-name and output folder by "filename" and "folder", respectively. In this sample, the source sheet is the sheet which opens currently. If you want to other sheet, please change this script.
If this will be helpful for you, I'm glad.
I could not get this to work either.
var csv = "";
var v = SpreadsheetApp
.getActiveSpreadsheet()
.getActiveSheet()
.getDataRange()
.getValues();
v.forEach(function(e) {
csv += e.join(",") + "\n";
});
I've never used the join method but I'm sure it's an intelligent approach however I decided to rewrite it in a much more simple minded approach. It doesn't handle the cases when there are commas in the fields and it doesn't put double quotes around any of the fields. To be quite honest about this sort of thing I almost always use ~~~ as my delimiter. Create columns with text to columns and life runs much smoother for me. In other words, if I can, I'll always take the easy way out.
Here's my version of just that section of code. Perhaps everything will run better for you now.
var csv = '';
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getDataRange().getValues();
for( var i = 0;i < v.length;i++)
{
for(var j = 0; j < v[i].length;j++)
{
if(j>0)
{
csv += ', ';
}
csv += v[i][j].toString();
}
csv += '\n';
}
The rest of Tanaike's code looks like it should work. If you have any further troubles come on back and I'll be glad to take another look at it.

Export Google Sheet as TXT file (Fixed Width)

I am trying to replicate some functionality from a Excel document with some macros in a Google Spreadsheet. The end goal is to export a fixed-width txt file. Unfortunately, the vendor cannot use a CSV file. So, does anybody know of a way to generate a fixed-width TXT file using Google Scripts?
Just in case anybody else comes looking. I used a few sources and came up with this:
function saveAsFixedWidthTxt() {
// get Spreadsheet Name
var fileName = SpreadsheetApp.getActiveSpreadsheet().getName();
//var fileName = SpreadsheetApp.getActiveSheet().getSheetName();
// get Directory the Spreadsheet is stored in.
var dirName = DocsList.getFileById(SpreadsheetApp.getActive().getId()).getParents()[0].getName();
// Add the ".txt" extension to the file name
fileName = fileName + ".txt";
// Convert the range data to fixed width format
var txtFile = convertRangeToTxtFile_(fileName);
// Delete existing file
deleteDocByName(fileName);
// Create a file in the Docs List with the given name and the data
DocsList.getFolder(dirName).createFile(fileName, txtFile);
}
function convertRangeToTxtFile_(txtFileName) {
try {
var txtFile = undefined;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var rows = sheet.getDataRange();
var data = rows.getValues();
// Loop through the data in the range and build a string with the data
if (data.length > 1) {
var txt = "";
for (var row = 2; row < data.length; row++) {
for (var j=6; j<=10; j++){
if(data[row][j] != ''){
// Employee ID
var empID = "" + data[row][3];
txt += empID;
//Fill to 6 characters
for (i=empID.length; i<6; i++){
txt += " ";
}
// Name
var fullName = data[row][5] + " " + data[row][4]
txt += fullName;
//Fill to 43 characters
for (i=fullName.length; i<44; i++){
txt += " ";
}
// etc, etc to build out your line
txt += "\r\n";
}
}
}
txtFile = txt;
}
return txtFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
function deleteDocByName(fileName){
var docs=DocsList.find(fileName)
for(n=0;n<docs.length;++n){
if(docs[n].getName() == fileName){
var ID = docs[n].getId()
DocsList.getFileById(ID).setTrashed(true)
}
}
}
This will work:
export the sheet as a xlsx, then open that file in Excel.
Export that file as Space Delimited Text (.prn, for some reason).
Change the file extension on the resultant file to .txt.
This will result in a file like this:
Column Another Column 3
Val 1 2 $ 5.00
Or do you need to get a .txt file directly out of Google Apps Script?
Edit since a direct .txt download is necessary.
Here's how you could design a script to do that:
Find a way to convert sheet.getColumnWidth(n) to a number of spaces. You may find that there are 5 pixels to 1 character, or whatever the ratio
Run getColumnWidth() for each column to find the width you need for each column. Alternatively, find the length longest string in each cell.
Go through each cell and add it to a large string you begin building. As you add it, add the number of spaces necessary to match the value converted from getColumnWidth(). At the end of each row, append \n to represent a new line.
Once the output string is complete, you could then use the Content Service to download the text like so:
ContentService.createTextOutput(youBigString).downloadAsFile('filename.txt')
This would involve deploying your script as a web app, which could work well - you'd go to a URL and the page would trigger a download of the fixed-width file.
In my case, DocList use returns ReferenceError.#MSCF your lines 7 & 27 :
var dirName = DocsList.getFileById(SpreadsheetApp.getActive().getId()).getParents()[0].getName();
DocsList.getFolder(dirName).createFile(fileName, txtFile);
become
var dirName = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).getParents().next().getId();
DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).getParents().next().createFile(fileName, txtFile);

How To Download / Export Sheets In Spreadheet Via Google Apps Script

The task is to automate the manual process accomplished by the menu option "File | Download As | Plain Text"
I want to be able to control the saved file name, which cannot be done via the menu.
At the time this is invoked, the user would be sitting on the sheet in the spreadsheet. Ultimately, I'd make it a menu option, but for testing I'm just creating a function that I can run manually.
After reading several other threads for possible techniques, this is what I've come up with.
It builds a custom name for the file, makes the call, and the response code is 200.
Ideally, I'd like to avoid the open / save dialog. In other words, just save the file without additional user intervention. I'd want to save in a specific folder and I've tried it with a complete file spec, but the result is the same.
If I copy the URL displayed in the Logger and paste it into a browser, it initiates the open / save dialog, so that string works.
Here's the code as a function.
function testExportSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var oSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sId = ss.getId();
var ssID=sId + "&gid=" + oSheet.getSheetId();
var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="
+ ssID + "&exportFormat=tsv";
Logger.log(url);
var fn = ss.getName() + "-" + oSheet.getSheetName() + ".csv";
var sHeaders = {"Content-Disposition" : "attachment; filename=\"" + fn + "\""};
var sOptions = {"contentType" : "text/html", "headers" : sHeaders};
Logger.log(sOptions);
x = UrlFetchApp.fetch(url, sOptions)
Logger.log(x.getResponseCode());
}
I have exported a spreadsheet as CSV directly into a local hard drive as follows:
Get the CSV content from current sheet using a variation of function convertRangeToCsvFile_() from the tutorial on this page https://developers.google.com/apps-script/articles/docslist_tutorial#section3
var csvFile = convertRangeToCsvFile_(...);
Then select a drive folder that is syncing to a local computer using Drive
var localFolder = DocsList.getFolderById("055G...GM");
And finally save the CSV file into the "local" folder
localFolder.createFile("sample.csv", csvFile);
That's it.
This app script returns a file for download instead of web page to display:
function doGet(){
var outputDocument = DocumentApp.create('My custom csv file name');
var content = getCsv();
var textContent = ContentService.createTextOutput(content);
textContent.setMimeType(ContentService.MimeType.CSV);
textContent.downloadAsFile("4NocniMaraton.csv");
return textContent;
}
In case you are looking to export all of the sheets in s spreadsheet to csv without having to manually do it one by one, here's another thread about it:
Using the google drive API to download a spreadsheet in csv format
The download can be done. But not the "Write to the hard drive" of the computer.
Write issue:
You mean write a file to the hard drive of the computer, using Google Apps Script? Sorry, but you will need more than GAS to do this. For security reasons, I doubt this is possible with only GAS, have never seen anything like this in GAS.
Google Drive API will let you do a download, just needs OAuth and the URL you gave.
This is the function that I found when looking up the same question. This function was linked to by #Mogsdad and the link no longer exists.
function convertRangeToCsvFile_(csvFileName) {
// Get the selected range in the spreadsheet
var ws = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection();
try {
var data = ws.getValues();
var csvFile = undefined;
// Loop through the data in the range and build a string with the CSV data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// Join each row's columns
// Add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
I found it here and here