10.2 GP service creates file - how to write it so I can actually retrieve it

4271
5
Jump to solution
05-29-2014 08:23 AM
FrankMcLean
New Contributor III
Hi,

I am tearing out my hair (which I can ill afford to do) over being able to retrieve a file I create via a GP service.  Very simplified:-

1. Let's say I have a Python script which takes no input parameters.
2. It creates a file called 'HelloWorld.txt', which is empty.

That's it.  This runs fine on the desktop and I can retrieve HelloWorld.txt.  Yippee.

When I publish this, successfully, and run it, successfully, I cannot for the life of me get the file.  I've tried a lot of stuff:

1. Writing to 'nowhere', i.e. just HelloWorld.txt = written in job scratch area, result url goes to a scratch folder which gives an HTTP 400, Invalid URL.
2. Writing to 'scratch' via arcpy.env.scratchFolder = same as above
3. Writing to the 'current workspace' via arcpy.env.workspace = empty on server, same as above
4. Etc

What it _appears_ I need to do is to somehow write to the 'Output Directory' specified for the service, which has a nice accompanying 'Virtual Directory'.  Am I right?  How on earth do I get this location?  Would doing that even cause the url to be mapped to that location?  Why is this so hard?  🙂  You just publish a GP result, right?  Hooray!

Sorry.  You can probably tell this has my mind in knots.

Please, if someone could provide the Python snippet that will do this, I would be most grateful.  I might even make a T Shirt out of it, grow a beard and travel the world evangelizing about it, because this should be EASY, front and center whenever someone outputs a file as a script result and wants to publish it.  I'm asking for a Python snippet, rather than a URL to help docs, because "I've been everywhere, man!" and really doubt those will shed any light.  DO NOT let this put you off attempting to help, however. 🙂  I'll be truly grateful for any crumbs.

I just wanted it to work like the print task works.  You get a nice URL that you download from and go on with your life.  This seemed like it must be a five minute job.  Tops.

Thanks for listening!

Frank
1 Solution

Accepted Solutions
KevinHibma
Esri Regular Contributor
I think you're falling into the mindset a lot of people do when trying to return a file.
My first bit of advice is to not over think it (yeah, easier said then done, I know).

If you have a tool in Desktop that works...ie, a python script tool that outputs a file, you have a parameter that either is output required or a derived output - either way, its of type FILE.
You run this tool in Desktop, open the results window, expand that successful result and there will be the output file you can double click. If you're output a txt file, double clicking it should open it in notepad (or whatever you PC opens .txt files with).

Now moving to Server, dont over think it assuming you need to return a URL or worry about virtual directories. The GP/Server framework handles all of this. The only thing that changes in terms of GP Server and file output is the client consuming the service. If your client happens to be ArcMap, run the tool from your server connection. Expand the result in the Results window and double click the result. Exact same experience...ArcMap as a client handled getting the file down from the Server and helped you open it.
If your client is the Services Directory (REST Endpoint), a successful result will return a link you can click to download the file.
If your client is a web app, you do need to write some code to handle getting the result and saving it (well telling the browser to popup a box and save it).

Apologies if I'm off base and you understand this bit - if your question is "how do I write the python code to output a file", well it should be handled as well. In short, your code could be this simple (assuming input parameter of string and derived output parameter of file)

import arcpy import os  getInputTxt = arcpy.GetParameterAsText(0)  txtFile = os.path.join(arcpy.env.scratchFolder, "myTxt.txt") f = open(txtFile, 'w') f.writelines(getInputTxt) f.close  arcpy.SetParameterAsText(1, txtFile)

View solution in original post

5 Replies
KevinHibma
Esri Regular Contributor
I think you're falling into the mindset a lot of people do when trying to return a file.
My first bit of advice is to not over think it (yeah, easier said then done, I know).

If you have a tool in Desktop that works...ie, a python script tool that outputs a file, you have a parameter that either is output required or a derived output - either way, its of type FILE.
You run this tool in Desktop, open the results window, expand that successful result and there will be the output file you can double click. If you're output a txt file, double clicking it should open it in notepad (or whatever you PC opens .txt files with).

Now moving to Server, dont over think it assuming you need to return a URL or worry about virtual directories. The GP/Server framework handles all of this. The only thing that changes in terms of GP Server and file output is the client consuming the service. If your client happens to be ArcMap, run the tool from your server connection. Expand the result in the Results window and double click the result. Exact same experience...ArcMap as a client handled getting the file down from the Server and helped you open it.
If your client is the Services Directory (REST Endpoint), a successful result will return a link you can click to download the file.
If your client is a web app, you do need to write some code to handle getting the result and saving it (well telling the browser to popup a box and save it).

Apologies if I'm off base and you understand this bit - if your question is "how do I write the python code to output a file", well it should be handled as well. In short, your code could be this simple (assuming input parameter of string and derived output parameter of file)

import arcpy import os  getInputTxt = arcpy.GetParameterAsText(0)  txtFile = os.path.join(arcpy.env.scratchFolder, "myTxt.txt") f = open(txtFile, 'w') f.writelines(getInputTxt) f.close  arcpy.SetParameterAsText(1, txtFile)
JeffreyWilkerson
Occasional Contributor II

I know this is an old post, but when I came across it it was exactly what I needed.  There needs to be more of this mindset ("don't overthink it") in the help text for ArcGIS development in general.  Until you do it for the first time, it's all foreign and doesn't exactly make sense.  Thanks for this gem Kevin.

0 Kudos
FrankMcLean
New Contributor III
Hi Kevin,

Well, that worked. 🙂

I removed the following, to see if that was the magic, but thankfully it appears to be a no-op, so no hardcoding of paths at all!

arcpy.env.scratchWorkspace = r"C:\temp\pppp"


Your answer has resulted in a sub-one-line change to my code so that it now works.  I could swear I was outputting to scratchFolder at some point, but I guess not.  I must have been caught up in the whirling hall of knives that is the actual function of the script and never noticed.

I think I now understand why 'setParameterAsText()' is your only choice and won't have my brain capsizing over the fact that, hey, it's a file, not a String!  And that it's the way you wire up the script to the toolbox that let's Arc know that if it sees a string, but is told it's a File, it should interpret this as a path.  Or a String and a Network Analyst Class Fieldmap and interpret this as heaven knows what.  I don't think this is intuitively obvious to the overthinkers out here.  It's certainly not spelled out anywhere.

So, one last question, if I may?  Since the whole point of this is to create something I can push into source control and deploy easily, would this be better in a Python Toolbox?  I believe you can encapsulate the parameter 'wirings' inside that, can't you?  Making one, small, neat piece of text in source control.

Thanks a lot for your excellent, very rapid response.

Frank
0 Kudos
KevinHibma
Esri Regular Contributor
Yeah, sorry about this...
arcpy.env.scratchWorkspace = r"C:\temp\pppp"


I realized after I push submit I had that in my code snippet: I write code in an IDE but like to know where my output is going, so I added that.
>Very brief back story on scratchFolder for you and anyone else reading....
When you run a tool in the app (arcmap for example), the scratchFolder and scratchGDB are derived from whatever the applications scratchWorkspace is. When you run a script from command line or an IDE - the scratchworkspace isn't set. So by default this gets set to a folder in your user accounts temp directory (its annoying to go find it). So I can control where the output of the scratchFolder gets put by setting the scratchWorkspace in the script.
For GP Services - scratchWorkspace doesn't matter, in fact its set and you can't change it. Its always created as a new directory with the GUID of the executing job. So its simply handled and works when you write to scratchFolder.

As for your understanding: yes. Its 2 parts. Its what you have in your script, but more importantly its the parameter type you've set on the tool itself. Like you said, in this case, file.

In terms of a PYT - yeah, probably go with that based on your requirements (yes you control params via code inside). The TBX itself is binary, so its unlikely your change system will be able to identify changes made to the file? So a PYT, basically being text would work well for you, I'd guess. Change system aside, PYT vs. TBX+Script for GP Server: it doesn't matter. For GP Services a tool is a tool is a tool. The guts underneath might be different, but these two scenarios may as well be the same thing, so I just say go with what you like most.
0 Kudos
FrankMcLean
New Contributor III
Thanks Kevin.  I'll go PYT.
0 Kudos