For Zoho Services only:


I'm actually part of something bigger at Ascent Business Solutions recognized as the top Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions offer support for smaller technical fixes and projects for larger developments, such as migrating to a ZohoCRM.  A team rather than a one-man-band is always available to ensure seamless progress and address any concerns. You'll find our competitive support rates with flexible, no-expiration bundles at https://ascentbusiness.co.uk/zoho-services/uk-zoho-support.  For larger projects, talk to our experts and receive dedicated support from our hands-on project consultants at https://ascentbusiness.co.uk/zoho-services/zoho-crm-implementation.

The team I manage specializes in coding API integrations between Zoho and third-party finance/commerce suites such as Xero, Shopify, WooCommerce, and eBay; to name but a few.  Our passion lies in creating innovative solutions where others have fallen short as well as working with new businesses, new sectors, and new ideas.  Our success is measured by the growth and ROI we deliver for clients, such as transforming a garden shed hobby into a 250k monthly turnover operation or generating a +60% return in just three days after launch through online payments and a streamlined e-commerce solution, replacing a paper-based system.

If you're looking for a partner who can help you drive growth and success, we'd love to work with you.  You can reach out to us on 0121 392 8140 (UK) or info@ascentbusiness.co.uk.  You can also visit our website at https://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 :: Article: 870

Add comment

Your rating:

Submit

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

Accreditation

Badge - Zoho Creator Certified Developer Associate
Badge - Zoho Deluge Certified Developer
Badge - Certified Zoho CRM Developer

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

Ethereum:
Donate to Joel Lipman with Ethereum 0xb038962F3809b425D661EF5D22294Cf45E02FebF

Please publish modules in offcanvas position.