Experience Builder Custom Widget - Upload File

1522
3
Jump to solution
12-10-2022 07:21 AM
Colome
by
New Contributor II

I'm trying to develop a widget to allow the upload of a DWG file, which will be the input of a Geoprocessing service. I have the Geoprocessing Service published with the 'upload' capability enabled and set to Asynchronous, and "Maximum Number of Records Returned by Server:" set to like 100,000

I am using Experience Builder developer edition 1.9, with ArcGIS Enterprise 10.9. I should also note it is written in Type Script.

Currently I am struggling to get the upload/upload portion of the Geoprocessing service to work. I've posted my code below. I am uing the jimu TextInput component, set to type="file", and I am trying to trigger the upload/upload function on the onChange trigger

DanielCaparrosMidwood2_0-1670679422949.png

I've tried using esriRequest as reading the following documentation, it seems to be what I need to upload a file. And I am passing the file object into the esriRequest as the "Body" which is what it states in the documentation. 

https://developers.arcgis.com/javascript/latest/api-reference/esri-request.html

import React from "react";

import {Label,TextInput} from "jimu-ui";
import { esriRequest } from "esri/request";

const AddFileTab = () => {
    const onFileInputChangeHandler = async function execute(evt😞 Promise<void> {
        const file_input = evt.target.value
        const url = "XXXX/GPServer/uploads/upload"

        esriRequest(url, {
            body: file_input,
            method : "post",
            responseType : "json"
        }).then((response) => {
            console.log(response); 
        })
    }

    const renderer = () => {
        return (
            <div className={""}>
                <Label >
                    Select file
                    </Label>
                    <TextInput
                        onAcceptValue={function noRefCheck(){}}
                        type="file"
                        onChange = {onFileInputChangeHandler}
                    />
            </div>
        )
    }
return renderer();
}

However, I get the following error

widget.tsx?cacc:16 Uncaught (in promise) TypeError: (0 , esri_request__WEBPACK_IMPORTED_MODULE_2__.esriRequest) is not a function
at Object.eval (widget.tsx?cacc:16:1)
at Generator.next (<anonymous>)
at eval (widget.tsx:14:71)
at new Promise (<anonymous>)
at __awaiter (widget.tsx:10:12)
at Object.execute [as onChange] (widget.tsx?cacc:11:1)
at index.js:3033:364
at Generator.next (<anonymous>)
at index.js:3010:1749
at new Promise (<anonymous>)

Can any suggest any solutions? Or would anyone be willing to share if they've managed to solve this functionality before? I have scoured the web but I can't find any other solutions specifically in Experience Builder and using TypeScript.

 

Any help would be greatly appreciated!

0 Kudos
1 Solution

Accepted Solutions
Grant-S-Carroll
Esri Contributor

Hi there, 

The error suggests that its not importing esriRequest correctly. Looking at what you have in your import statement, you are trying to import a non-default export by using the {}, esriRequest, is the default export, can you try changing the import statement to 

import esriRequest from 'esri/request'

 

instead of 

import {esriRequest} from 'esri/request'

 

Below is a utils file we have that we use for the GP services that require upload functionality.

import { getAppStore } from "jimu-core";
import esriRequest from "esri/request";
import Geoprocessor from 'esri/tasks/Geoprocessor';

export default class GeoprocessingUtils {
    uploadUrl: string;
    token: string;
    gp: Geoprocessor;

    constructor(url: string, isUploadSupported: boolean = false) {
        this.token = getAppStore().getState().token;
        const serverUrl = getAppStore().getState().portalUrl.replace("portal", "server");
        const gpUrl = serverUrl + url + "?token=" + this.token;

        this.gp = new Geoprocessor({ url: gpUrl });
        if (isUploadSupported) {
            const urlArr = url.split("/GPServer/");
            this.uploadUrl = (urlArr.length > 1) ? serverUrl + urlArr[0] + "/GPServer/uploads/upload" : "";
        } else {
            this.uploadUrl = "";
        }
    }

    uploadFile = async (file: any): Promise<string> => {
        let form = new FormData();
        form.append('file', file);
        form.append("f", "json");
        form.append("title", file.name);
        form.append("name", file.name);
        form.append("type", file.type);
        form.append("token", this.token);
        form.append("filename", file.name);

        let upload = await esriRequest(this.uploadUrl, {
            method: "post", body: form
        }).then(this.uploadSucceeded, this.uploadFailed)
            .catch((error) => {
                console.error('An error occured uploading the file: ' + error);
                return null;
            });

        if (upload) {
            return upload;
        } else {
            return null;
        }

    }

    executeTaskAsyn = async (params: any): Promise<any> => {
        let response = await this.gp.submitJob(params)
            .then(async (jobInfo) => {
                console.log(jobInfo);
                var jobid = jobInfo.jobId;
                var options = {
                    interval: 1500,
                    statusCallback: function (j) {
                        console.log("Job Status: ", j.jobStatus);
                    }
                };
                return this.gp.waitForJobCompletion(jobid, options).then(async () => {
                    return this.gp.getResultData(jobid, "result").then((result) => {
                        return result.value;
                    }).catch(error => {
                        console.error(error);
                        return { "error": "Failed to run geoprocessing task" };
                    });
                   
                });
                
            })
            .catch((error) => {
                console.error(error)
                let errorMessages = error.messages.filter(msg => msg.type === 'error');
                //the first error is the error message from gp output parameter
                if (errorMessages.length > 0) {
                    return { "error": errorMessages[0].description }
                } else {
                    return { "error": "Failed to run geoprocessing task" };
                }
            });

        if (response) {
            return response;
        }
    }

    uploadSucceeded = (response) => {
        var itemID = response["data"]["item"].itemID;
        console.log("File upload successful, item ID: ", itemID);
        return itemID;
    }

    uploadFailed = (response) => {
        console.log("Failed: ", response);
        return null;
    }
}

 

Hope that helps.

View solution in original post

0 Kudos
3 Replies
Grant-S-Carroll
Esri Contributor

Hi there, 

The error suggests that its not importing esriRequest correctly. Looking at what you have in your import statement, you are trying to import a non-default export by using the {}, esriRequest, is the default export, can you try changing the import statement to 

import esriRequest from 'esri/request'

 

instead of 

import {esriRequest} from 'esri/request'

 

Below is a utils file we have that we use for the GP services that require upload functionality.

import { getAppStore } from "jimu-core";
import esriRequest from "esri/request";
import Geoprocessor from 'esri/tasks/Geoprocessor';

export default class GeoprocessingUtils {
    uploadUrl: string;
    token: string;
    gp: Geoprocessor;

    constructor(url: string, isUploadSupported: boolean = false) {
        this.token = getAppStore().getState().token;
        const serverUrl = getAppStore().getState().portalUrl.replace("portal", "server");
        const gpUrl = serverUrl + url + "?token=" + this.token;

        this.gp = new Geoprocessor({ url: gpUrl });
        if (isUploadSupported) {
            const urlArr = url.split("/GPServer/");
            this.uploadUrl = (urlArr.length > 1) ? serverUrl + urlArr[0] + "/GPServer/uploads/upload" : "";
        } else {
            this.uploadUrl = "";
        }
    }

    uploadFile = async (file: any): Promise<string> => {
        let form = new FormData();
        form.append('file', file);
        form.append("f", "json");
        form.append("title", file.name);
        form.append("name", file.name);
        form.append("type", file.type);
        form.append("token", this.token);
        form.append("filename", file.name);

        let upload = await esriRequest(this.uploadUrl, {
            method: "post", body: form
        }).then(this.uploadSucceeded, this.uploadFailed)
            .catch((error) => {
                console.error('An error occured uploading the file: ' + error);
                return null;
            });

        if (upload) {
            return upload;
        } else {
            return null;
        }

    }

    executeTaskAsyn = async (params: any): Promise<any> => {
        let response = await this.gp.submitJob(params)
            .then(async (jobInfo) => {
                console.log(jobInfo);
                var jobid = jobInfo.jobId;
                var options = {
                    interval: 1500,
                    statusCallback: function (j) {
                        console.log("Job Status: ", j.jobStatus);
                    }
                };
                return this.gp.waitForJobCompletion(jobid, options).then(async () => {
                    return this.gp.getResultData(jobid, "result").then((result) => {
                        return result.value;
                    }).catch(error => {
                        console.error(error);
                        return { "error": "Failed to run geoprocessing task" };
                    });
                   
                });
                
            })
            .catch((error) => {
                console.error(error)
                let errorMessages = error.messages.filter(msg => msg.type === 'error');
                //the first error is the error message from gp output parameter
                if (errorMessages.length > 0) {
                    return { "error": errorMessages[0].description }
                } else {
                    return { "error": "Failed to run geoprocessing task" };
                }
            });

        if (response) {
            return response;
        }
    }

    uploadSucceeded = (response) => {
        var itemID = response["data"]["item"].itemID;
        console.log("File upload successful, item ID: ", itemID);
        return itemID;
    }

    uploadFailed = (response) => {
        console.log("Failed: ", response);
        return null;
    }
}

 

Hope that helps.

0 Kudos
Colome
by
New Contributor II

Excellent - thank you. Changing the " import esriRequest" worked. And the way you format the Params was what I need.

Thanks again

0 Kudos
MarkEastwood
Occasional Contributor II

@Colome would you be willing to share your updated code to show how you got it working. I'm running into a similar issue and am struggling to get the upload to work. 

0 Kudos