For Zoho services only


I'm currently part of a wider delivery team at Ascent Business Solutions, recognised as a leading Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions support organisations with everything from targeted technical fixes through to full Zoho CRM implementations and long-term platform adoption. Working as a team rather than a one-person consultancy allows projects to move forward consistently, with access to the right skills at each stage.

The team I manage specialises in API integrations between Zoho and third-party finance and commerce platforms such as Xero, Shopify, WooCommerce, and eBay. Much of our work involves solving integration challenges that fall outside standard documentation, supporting new ideas, new sectors, and evolving business models.

Success is measured through practical outcomes and return on investment, ranging from scaling small operations into high-turnover businesses to delivering rapid gains through online payments, automation, and streamlined digital workflows.

If you are looking for structured Zoho expertise backed by an established consultancy, you can contact Ascent Business Solutions on 0121 392 8140 (UK), email info@ascentbusiness.co.uk, or visit https://www.ascentbusiness.co.uk.
ZohoCreator: Using .toFile and Uploading to a Creator field

ZohoCreator: Using .toFile and Uploading to a Creator field

What?
This is a quick article on generating a file with the Zoho Deluge function .toFile() and uploading it to a file upload field within Zoho Creator.

Why?
The use-case here is that we want a file to be hosted within Zoho Creator for use by a JS widget that will download the CSV (in this case a Text *.txt) file for use by a dynamic auto-suggester dropdown; one of those fancy ones that includes photos and a bit of fuzzy logic.

I thought I could simply update the field with the new file but as I'm not downloading it, this is apparently not the process.

How?
As a high-level overview: we'll generate the CSV rows and then a file; then we'll use invokeURL to upload the file.

Generating the CSV
Here's the simplified code to generate the CSV - note that I am replacing commas with the HTML entity equivalent and enclosing the ID in double-quotes in case I open it in MS Excel or another system that likes rounding 64-bit signed integers (long numbers):
copyraw
//
	// Generate a CSV of a Report in ZohoCreator
	l_CsvLines = List();
	l_ApplicableProducts = Products[Display_in_Widget=="Yes"] sort by Product_Name;
	for each c_Product in l_ApplicableProducts
    {
		l_CsvRow = List();
		l_CsvRow.add(c_Product.Photo);
		l_CsvRow.add(c_Product.Product_Name.replaceAll(",", ",", true));
		l_CsvRow.add(c_Product.Product_Desc.replaceAll(",", ",", true));
		l_CsvRow.add("\"" + c_Product.ID + "\"");
		l_CsvLines.add(l_CsvRow.toString());
    }
  1.  // 
  2.      // Generate a CSV of a Report in ZohoCreator 
  3.      l_CsvLines = List()
  4.      l_ApplicableProducts = Products[Display_in_Widget=="Yes"] sort by Product_Name; 
  5.      for each c_Product in l_ApplicableProducts 
  6.      { 
  7.          l_CsvRow = List()
  8.          l_CsvRow.add(c_Product.Photo)
  9.          l_CsvRow.add(c_Product.Product_Name.replaceAll(",", ",", true))
  10.          l_CsvRow.add(c_Product.Product_Desc.replaceAll(",", ",", true))
  11.          l_CsvRow.add("\"" + c_Product.ID + "\"")
  12.          l_CsvLines.add(l_CsvRow.toString())
  13.      } 

Generate the file
You may note that this generates a text file rather than a CSV, this is because our JS widget won't pack a CSV but it will pack text files.
copyraw
//
	// generate a CSV or TXT
	v_CSVFilename = "applicable_products.txt";
	f_CSVFile = l_CsvLines.toString(zoho.encryption.urlDecode("%0A")).toFile(v_CSVFilename);
	f_CSVFile.setParamName("file");
  1.  // 
  2.      // generate a CSV or TXT 
  3.      v_CSVFilename = "applicable_products.txt"
  4.      f_CSVFile = l_CsvLines.toString(zoho.encryption.urlDecode("%0A")).toFile(v_CSVFilename)
  5.      f_CSVFile.setParamName("file")

Get current file name (optional)
To check on filename changes, I run this bit of code to get the internal and current file name:
copyraw
//
	// get original document file (optional) - you need your own record ID here
	v_ZC_DocumentRecordID = 1234567890123456789;
	c_ExistingDocument = Document[ID == v_ZC_DocumentRecordID];
	info c_ExistingDocument.Document_File;
  1.  // 
  2.      // get original document file (optional) - you need your own record ID here 
  3.      v_ZC_DocumentRecordID = 1234567890123456789
  4.      c_ExistingDocument = Document[ID == v_ZC_DocumentRecordID]
  5.      info c_ExistingDocument.Document_File; 

Upload the generated file to the record
Note that this is overwriting the existing record and associated file. We have no intention of creating a new record per file... Saying that, the file name will change every time it is updated as you will see. Also note, I have a form called "Document", a report called "All Documents", and the file upload field is called "Document File":
copyraw
//
	// now upload updated file
	v_Endpoint = "https://creator.zoho.com/api/v2.1"+zoho.appuri+"report/All_Documents/"+v_ZC_DocumentRecordID+"/Document_File/upload";
	r_Upload = invokeurl
	[
		url :v_Endpoint
		type :POST
		files: f_CSVFile
		connection:"zcreator"
	];
	info r_Upload;
  1.  // 
  2.      // now upload updated file 
  3.      v_Endpoint = "https://creator.zoho.com/api/v2.1"+zoho.appuri+"report/All_Documents/"+v_ZC_DocumentRecordID+"/Document_File/upload"
  4.      r_Upload = invokeurl 
  5.      [ 
  6.          url :v_Endpoint 
  7.          type :POST 
  8.          files: f_CSVFile 
  9.          connection:"zcreator" 
  10.      ]
  11.      info r_Upload; 

Display new file name (optional)
copyraw
//
	// get updated document file (optional: integrity check)
	c_ExistingDocument = Document[ID == v_ZC_DocumentRecordID];
	info c_ExistingDocument.Document_File;
  1.  // 
  2.      // get updated document file (optional: integrity check) 
  3.      c_ExistingDocument = Document[ID == v_ZC_DocumentRecordID]
  4.      info c_ExistingDocument.Document_File; 

I have all of the above in the same function. I've split it out to explain parts but otherwise it is intended to be all in the same function as there are references to some variables in earlier snippets of code on this page.

Error(s):
  • 2945: UPLOAD_RULE_NOT_CONFIGURED
    Was happening when I was trying to update the Zoho Creator record, specifically the file upload field, with the file object directly, eg. <collection>.<field>=<file_object>.
  • 2945: UPLOAD_RULE_NOT_CONFIGURED
    Also got this error if I failed to add the setParamName such as:
    copyraw
    f_CSVFile.setParamName("file");
    1.  f_CSVFile.setParamName("file")
  • 3700: Unable to upload a file. Please check and try again.
    If I test my connection or even change how I upload the file, this error no longer appears. This error seems to be at the end of a function which has already done a lot of processing. Here's a quick test snippet of code:
    copyraw
    void fn_UploadTest()
    {
    	//
    	// selecting a record which has a file upload field that we want to upload to
    	c_Check = My_Form[ID == 123456];
    	if(c_Check.count() > 0)
    	{
    		//
    		// A quick CSV line
    		f_TestFile = "Test 1,Test 2,Test 3".toFile("test.csv");
    		f_TestFile.setParamName("file");
    		//
    		// found if you open the report properties and select rename from the vertical ellipsis icon in the top right
    		v_ReportLinkName = "All_Weekly_Calendar_Caches";
    		v_FileUploadFieldName = "Cached_DataFile";
    		v_RecordID = c_Check.ID.toString();
    		//
    		// using API upload file endpoint (either endpoints seem to be valid)
    		//v_Endpoint = "https://creator.zoho.com/api/v2.1"+zoho.appuri+"report/"+v_ReportLinkName+"/"+v_RecordID+"/"+v_FileUploadFieldName+"/upload";
    		v_Endpoint = "https://www.zohoapis.com/creator/v2.1/data"+zoho.appuri+"report/"+v_ReportLinkName+"/"+v_RecordID+"/"+v_FileUploadFieldName+"/upload?skip_workflow=['all']";
    		info v_Endpoint;
    		//
    		// upload the file
    		m_Params = Map();
    		m_Params.put("file", f_TestFile);
    		//
    		// this will work
    		r_Upload = invokeurl
    		[
    			url :v_Endpoint
    			type :POST
    			parameters: m_Params
    			content-type: "multipart/form-data"
    			connection:"zcreator"
    		];
    		//
    		// this also works
    		r_Upload = invokeurl
    		[
    			url :v_Endpoint
    			type :POST
    			files: f_TestFile
    			connection:"zcreator"
    		];
    		info r_Upload;
    	}	
    }
    1.  void fn_UploadTest() 
    2.  { 
    3.      // 
    4.      // selecting a record which has a file upload field that we want to upload to 
    5.      c_Check = My_Form[ID == 123456]
    6.      if(c_Check.count() > 0) 
    7.      { 
    8.          // 
    9.          // A quick CSV line 
    10.          f_TestFile = "Test 1,Test 2,Test 3".toFile("test.csv")
    11.          f_TestFile.setParamName("file")
    12.          // 
    13.          // found if you open the report properties and select rename from the vertical ellipsis icon in the top right 
    14.          v_ReportLinkName = "All_Weekly_Calendar_Caches"
    15.          v_FileUploadFieldName = "Cached_DataFile"
    16.          v_RecordID = c_Check.ID.toString()
    17.          // 
    18.          // using API upload file endpoint (either endpoints seem to be valid) 
    19.          //v_Endpoint = "https://creator.zoho.com/api/v2.1"+zoho.appuri+"report/"+v_ReportLinkName+"/"+v_RecordID+"/"+v_FileUploadFieldName+"/upload"; 
    20.          v_Endpoint = "https://www.zohoapis.com/creator/v2.1/data"+zoho.appuri+"report/"+v_ReportLinkName+"/"+v_RecordID+"/"+v_FileUploadFieldName+"/upload?skip_workflow=['all']"
    21.          info v_Endpoint; 
    22.          // 
    23.          // upload the file 
    24.          m_Params = Map()
    25.          m_Params.put("file", f_TestFile)
    26.          // 
    27.          // this will work 
    28.          r_Upload = invokeurl 
    29.          [ 
    30.              url :v_Endpoint 
    31.              type :POST 
    32.              parameters: m_Params 
    33.              content-type: "multipart/form-data" 
    34.              connection:"zcreator" 
    35.          ]
    36.          // 
    37.          // this also works 
    38.          r_Upload = invokeurl 
    39.          [ 
    40.              url :v_Endpoint 
    41.              type :POST 
    42.              files: f_TestFile 
    43.              connection:"zcreator" 
    44.          ]
    45.          info r_Upload; 
    46.      } 
    47.  } 
    Additional: I thought having a file and then using ZohoCreator .content would be a good way to store data exceeding 64Kb but instead I found using additional multi-line fields performance-wise was better:
    copyraw
    //
    		// bigger than 64kb
    		c_Check.Cached_HTML = v_HtmlToCache;
    		if(v_HtmlToCache.len()>65000)
    		{
    			c_Check.Cached_HTML = v_HtmlToCache.subString(0,65000); 
    			c_Check.Cached_HTML_2 = v_HtmlToCache.subString(65000); 
    		}
    		if(v_HtmlToCache.len()>129999)
    		{
    			c_Check.Cached_HTML_2 = v_HtmlToCache.subString(65000,130000); 
    			c_Check.Cached_HTML_3 = v_HtmlToCache.subString(130000); 
    		}
    1.  // 
    2.          // bigger than 64kb 
    3.          c_Check.Cached_HTML = v_HtmlToCache; 
    4.          if(v_HtmlToCache.len()>65000) 
    5.          { 
    6.              c_Check.Cached_HTML = v_HtmlToCache.subString(0,65000)
    7.              c_Check.Cached_HTML_2 = v_HtmlToCache.subString(65000)
    8.          } 
    9.          if(v_HtmlToCache.len()>129999) 
    10.          { 
    11.              c_Check.Cached_HTML_2 = v_HtmlToCache.subString(65000,130000)
    12.              c_Check.Cached_HTML_3 = v_HtmlToCache.subString(130000)
    13.          } 

Category: Zoho Creator :: Article: 399

Credit where Credit is Due:


Feel free to copy, redistribute and share this information. All that we ask is that you attribute credit and possibly even a link back to this website as it really helps in our search engine rankings.

Disclaimer: Please note that the information provided on this website is intended for informational purposes only and does not represent a warranty. The opinions expressed are those of the author only. We recommend testing any solutions in a development environment before implementing them in production. The articles are based on our good faith efforts and were current at the time of writing, reflecting our practical experience in a commercial setting.

Thank you for visiting and, as always, we hope this website was of some use to you!

Kind Regards,

Joel Lipman
www.joellipman.com