ArcGIS Server with JavaScript API Blog

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Latest Activity

(2 Posts)
mdonnelly
Esri Contributor

Hello,

Recently I needed to upload a file to a geoprocessing service using the ArcGIS JavaScript API V4 but had trouble finding a concise road map on how to achieve this. After knocking my knees on the undergrowth I am writing this blog post so you can travel the road in comfort.

I note that others have provided information for this using v3 of the API but there are some differences that this post explores relating to v4.

The Problem:

  • Need to upload a file to a geoprocessing service
  • Using the ArcGIS JavaScript API v4 on the client

The Solution Overview:

  • Create a geoprocessing tool that takes a file as a parameter
  • Publish the tool as a geoprocessing service with the option to Upload enabled
  • In the JS code have a way of prompting the user to upload a file using a document form
  • Create a request to the Upload endpoint of the geoprocessing service, whose body is the contents of the form
  • An item ID will be returned from the request. This ID is then passed as the file parameter to the geoprocessing service job
  • The geoprocessing service reads the ID from the file parameter and automatically translates that to the path to the uploaded file location

Solution Details

Python:

There isn't anything special from the Python side of things. Simply grab the file parameter as text:

file_path = arcpy.GetParameterAsText(0)

 

The parameter will be a string that represents the path to the file. Once you have the path to the file you can manipulate the file as required for your purposes.

Geoprocessing Tool:

Create a geoprocessing tool in ArcMap or ArcPro with a parameter for a file with a data type of 'File'.

Matching the order of the parameter in the tool with the parameter index in the Python script is essential. If the file parameter is first in the toolbox list then this parameter has an index of zero in the Python parameter list.

tool parameters

This translates to the following in Python:

arcpy.GetParameterAsText(0)

Geoprocessing Service:

Run the tool in ArcMap or ArcPro so that it completes successfully.

In ArcMap, go to Geoprocessing Results, right click on the successful job and select Share As > Geoprocessing Service

In ArcPro, go to History, right click on the successful job and select Share As > Web Tool

To enable Upload capability:

  • ArcMap: In the service editor, under the Parameters section, tick the Upload box
  • ArcPro: In the share as web tool dialog, in the Configuration tab, tick the Upload box

Check the tool parameters are correct and add descriptions. Publish the tool, which now has the ability to upload files.

JavaScript & HTML

1. Use a document form to allow the user to nominate a file for upload:

<form enctype="multipart/form-data" method="post" id="uploadForm">
   <div class="field">
      <label class="file-upload">
         <span><strong>Add File</strong></span>
         <input type="file" name="file" id="inFile" />
      </label>
   </div>
</form>

2. Create a request to the geoprocessing service upload endpoint, that uses the form as the body:

document
 .getElementById("uploadForm")
 .addEventListener("change", function(event) {
   var fileName = event.target.value.toLowerCase();
   if (fileName.indexOf(".zip") !== -1) {
      //is file a zip - if not notify user
      uploadFile(fileName);
   } 
 }
 });

function uploadFile(fileName) {
    var name = fileName.split(".");
    // Chrome and IE add c:\fakepath to the value - we need to remove it
    // see this link for more info: http://davidwalsh.name/fakepath
    name = name[0].replace("c:\\fakepath\\", "");
    var upload = esriRequest(
       fileUploadUrl,
       {
          body: document.getElementById("uploadForm"),
          method: "post",
          responseType: "text" //Ideally would return as json but that doesn't seem to work for me
       }      
    ).then(uploadSucceeded, errBack);
 }
  

3. Handle the response of the request and extract the item ID, passing it as a parameter to the geoprocessing service:

function uploadSucceeded(result) {
 
 //Ideally the result would come back as json but as it is coming back as html it will need to be parsed for the itemid
 var element = document.createElement('html');
 var itemID = "";
 var itemURL = "";
 element.innerHTML = result.data;
 var tdElements = element.getElementsByTagName('td');

 for (let tdElement of tdElements){
    if (tdElement.innerHTML == "Item ID:"){
       itemID = tdElement.nextElementSibling.firstChild.innerHTML;
    }
 }

 if (itemID == ""){
    console.log("Could not get file item id");
    return
 }
 
 console.log("itemID: " + itemID)
 var params = {
    FileName: "{'itemID':'" + itemID + "'}" //Replaces the need for the DataFile object
 };

 gp.submitJob(params).then(handleResult, errBack, progTest);
}


Note that the JS API documentation recommends that you create a DataFile object, which you then pass as the parameter value. I could not get that to work and so constructed the object from JSON myself based on what I could see the geoprocessing service required. I determined the format of the JSON object by executing the geoprocessing service from the REST endpoint.

I have attached my full sample JS code for reference. This code not only uploads a file but also gets a response in the form of an object id of the uploaded feature. The feature is zoomed to and then a FeatureForm for that feature is opened for editing.  One caveat is that at the time of writing I could not get the update button to work on the FeatureForm, probably due to an unresolved asych issue.

more
5 3 4,343
TomWayson
Esri Contributor

This post is aimed at helping you decide when to use the new @arcgis/webpack-plugin or esri-loader in applications built with webpack. I've recently had the opportunity to put the new webpack plugin through it's paces, and I came away with the impression that Rene Rubalcava, Yann Cabon‌, and the team have taken an important step forward in making the ArcGIS API for JavaScript more interoperable with other JavaScript frameworks and build tools.

TLDR: Scroll down to see the Conclusion.

Rumors of esri-loader's demise have been greatly exaggerated

So you may be wondering why would you ever want to use esri-loader now that we have the plugin?

First, if you're not using at least v4.7 of the ArcGIS API for JavaScript, you can stop reading this and head right on over to the esri-loader documentation. It also goes without saying that if you're using another module bundler besides webpack (such as rollup.js or parcel) that the webpack plugin won't be able to help you and that you'll need esri-loader instead. If you're developing Ember.js applications, which use Broccoli.js instead of webpack, head on over to ember-esri-loader.

Functional parity? Very close...

Still here? Great! Before getting into the details, I should start by identifying the ArcGIS API + webpack scenarios for which there was previously no other workable solution besides esri-loader. These scenarios are listed from more common to more advanced:

  1. You do not have access to the weback configuration (i.e. you were using a framework's CLI tool)
  2. You want to lazy-load the ArcGIS API after the rest of the application was loaded and rendered
  3. You want to use the ArcGIS API in a server-side rendered (SSR) application

Handles the hard stuff well

I'm happy to report that the new webpack plugin handles the more advanced scenarios (lazy-loading and use in a SSR application) very well! For each of the plugin's two demo applications I was able to make a branch that demonstrates how to lazy-load the esri bundles using dynamic import(). Here are the relevant TypeScript and Babel commits. I also able to confirm that each of those branches had roughly the same initial load performance as their esri-loader equivalents. Importantly, using lazy-loading (with either the webpack plugin or esri-loader) resulted in a 4x decrease in the time it took to see anything other than a blank white screen. Stephen Sylvia‌ expanded on that idea by creating a lightning fast (loads on a mobile phone on 3G in under 2 seconds) prototype that uses placeholder images for maps until the user wants to interact with them. Finally, I riffed on his work and created a branch that uses server-side rendering to demonstrate how you could optimize such a site for SEO. It would be easy to add a commit to that branch that leverages sever-side rendered Open Graph meta tags for rich social media sharing.

Some work to be done

However, these are still early days for the webpack plugin, and there are a few kinks to be worked out so that you can just use it in any webpack project. Specifically, at a bare minimum you need to be able to add it to the webpack config's array of plugins. The CLIs are getting better these days about letting you have access to the webpack config, but worst case scenario you may need to "eject" from the CLI in order to do so. You may find that even once you are able to add the plugin to your webpack config, some of it's settings conflict with those of other plugins added by the CLI. I had this problem when trying to add the webpack plugin to a create-react-app application. I poked and prodded a bit, and was able to resolve certain issues, but others were beyond my webpack fu (which isn't saying much).

Coming soon...

All in all though, I am confident that these incompatibilities will be worked out soon, and we'll have functional parity between the webpack plugin and esri-loader. If you're using the ArcGIS API with angular-cli, vue-cli, create-react-app, etc I hope you'll jump into the fray and try to help us figure out how to resolve these types of issues.

So, which should I use?

Let's say you've rolled your own webpack config (rather than starting from a CLI) and you can use either the webpack plugin or esri-loader in your app today. Why would you choose one or the other? Generally, I'd say that esri-loader makes it easier to integrate the ArcGIS API into a webpack application, because there is zero configuration. However it forces you into using complicated async patterns when working with 'esri' modules. In contrast, I'd say that the webpack plugin may be a bit more complicated to set up and configure, but once you do it offers more flexibility. Also, the webpack plugin does result in increased build times, but they're working on that, but coming from the ember-cli, I barely noticed.

import and import()

The biggest difference is that only the webpack plugin allows you to use import statements for esri modules. Instead esri-loader's asynchronous loadModules() API forces developers to consider the cost of loading esri modules and to use strategies that defer that cost by consolidating their use. This works well in applications that make very simple use of the ArcGIS API, such as a single map component. However this can become cumbersome in an application that has multiple components that work with the ArcGIS API such as a map component, a legend component, and a drawing toolbar, etc. Those types of applications will be easier to develop using the webpack plugin and import statements. However, if you want to lazy-load the ArcGIS API and it's modules (and if you're targeting mobile you should), you won't be able to freely use import statements anyway. Instead you'll end up grouping them into one or a few modules that you lazy-load with dynamic import() and you'll probably end up creating your own internal async API that looks a lot like that of esri-loader! Hopefully someday we'll be able to just use import statements for esri modules w/o either of these libraries and have support for things like tree-shaking of the ArcGIS API modules. To that end, the webpack plugin is more future-proof than esri-loader.

Conclusion

Here's a side-by-side comparison of the high level capabilities.

Capabilityesri-loader@arcgis/webpack-plugin
Works with ArcGIS API < v4.7YesNo
Works with other bundlers besides webpackYesNo
"Just works" with any framework CLI or webpack pluginYesNot yet
Can lazy-load the ArcGIS API and it's modulesYesYes
Can be used in a server-side rendered applicationYesYes
Can import esri modulesNoYes
Is future-proofNoYes

When you look at that list, it becomes clear that that esri-loader is the versatile, backwards-compatible fallback solution, and  @arcgis/webpack-plugin is a forward-looking link toward a brighter future. If you are able to give the plugin a try in your application I recommend that you do. If not, or if it doesn't work out for you, esri-loader will always be here for you.

more
2 0 3,739
15 Subscribers