J'ai souvent rencontré des questions concernant le téléchargement des pièces jointes (par exemple, photos et documents) à partir des couches d'entités ArcGIS Online. L'objectif est de fournir une solution alternative à ce qui est actuellement proposé.<\/P>
<\/P>
Premièrement, la seule façon dont les pièces jointes sont incluses lors de l'exportation des données via ArcGIS Online est lors de l'exportation en tant que File Geodatabase. La raison en est que le File Geodatabase est le seul format qui prend en charge les enregistrements liés. Par conséquent, la réponse la plus courante pour accéder aux pièces jointes des couches d'entités en dehors d'ArcGIS Online est d'exporter la couche d'entités en tant que File Geodatabase, puis d'extraire les données à l'aide d'un script comme celui-ci<\/A> via ArcGIS Desktop. Il existe également un script de synchronisation<\/A> disponible qui peut synchroniser un File Geodatabase local avec une couche d'entités hébergée. Ces solutions fonctionnent bien, mais elles dépendent d'ArcGIS Desktop, et dans le cas de la première option, nécessitent également de télécharger le File Geodatabase (qui peut augmenter en taille à mesure que vous y ajoutez des données) avant d'exécuter le script.<\/P><\/P>Pour cette raison, j'ai créé un script utilisant la nouvelle ArcGIS API for Python<\/A> qui fonctionne indépendamment d'ArcGIS Desktop. En fait, il fonctionne directement avec un Web GIS, donc vous n'avez même pas besoin de télécharger un File Geodatabase pour accéder à vos pièces jointes. Il peut être relancé régulièrement et ne télécharge sur le disque que les nouvelles pièces jointes si elles n'ont pas été téléchargées auparavant.<\/P><\/P>Que fait ce script ?<\/STRONG><\/P><\/P>Crée un dossier dans lequel les pièces jointes des entités sont stockées (par exemple Attachment Downloads)<\/LI>Dans ce dossier, crée un sous-dossier pour chaque couche dans la couche d'entités spécifiéeSi la variable AttachmentStorage est définie sur 'GroupedFolder', les pièces jointes sont stockées au format ObjectId-AttachmentId-OriginalFileName dans un seul dossier<\/LI>Si la variable AttachmentStorage est définie sur 'IndividualFolder', un nouveau dossier est créé pour chaque entité avec une pièce jointe (nommé selon l'ID de l'objet de l'entité), tandis que les pièces jointes sont stockées au format AttachmentId-OriginalFileName<\/LI><\/OL><\/LI>Si la pièce jointe existe déjà sur le disque, elle ne sera pas téléchargée à nouveau<\/LI>Résumé des téléchargements (nombre total de téléchargements et taille totale des pièces jointes) fourni en sortie finale dans la console<\/LI><\/OL><\/P>Comment s'installer ?<\/STRONG><\/P><\/P>Suivez le guide Install and set up<\/A> de l'ArcGIS API for PythonSi vous utilisez Anaconda, vous pouvez utiliser le fichier .yml pour créer l'environnement approprié<\/LI><\/OL><\/LI>Si vous utilisez le fichier Startup.bat, modifiez-le (dans un éditeur de texte) pour référencer les dossiers appropriés<\/LI>Une fois que vous avez ouvert un Jupyter Notebook, collez le fichier .zip ci-joint dans un emplacement accessible (et décompressez-le)<\/LI>Ouvrez le fichier DownloadAttachments.ipynb via Jupyter Notebooks et exécutez-le (cliquez dans le code et appuyez sur Ctrl+Enter)<\/LI>Le script s'exécutera sur la couche d'entités publique spécifiée et téléchargera les pièces jointes dans le dossier Downloads spécifié<\/LI>Un fichier journal associé est créé dans le dossier Logging chaque fois que vous exécutez le script<\/LI><\/OL><\/P>Notes :<\/STRONG><\/P><\/P>Mettez à jour la variable FeatureLayerId pour exécuter sur votre propre couche d'entités<\/LI>Spécifiez votre PortalUserName, PortalPassword si la couche d'entités est sécurisée<\/LI>Les noms des dossiers sont filtrés pour ne montrer que les caractères 0-9 et a-Z<\/LI>Ce script n'a pas été testé avec ArcGIS Enterprise (seulement les couches d'entités hébergées dans ArcGIS Online), mais il devrait quand même fonctionner<\/LI>Le code est également disponible via le dépôt GitHub Esri developer-support<\/A><\/LI><\/UL><\/BODY><\/HTML>
Pour cette raison, j'ai créé un script utilisant la nouvelle
Que fait ce script ?<\/STRONG><\/P><\/P>Crée un dossier dans lequel les pièces jointes des entités sont stockées (par exemple Attachment Downloads)<\/LI>Dans ce dossier, crée un sous-dossier pour chaque couche dans la couche d'entités spécifiéeSi la variable AttachmentStorage est définie sur 'GroupedFolder', les pièces jointes sont stockées au format ObjectId-AttachmentId-OriginalFileName dans un seul dossier<\/LI>Si la variable AttachmentStorage est définie sur 'IndividualFolder', un nouveau dossier est créé pour chaque entité avec une pièce jointe (nommé selon l'ID de l'objet de l'entité), tandis que les pièces jointes sont stockées au format AttachmentId-OriginalFileName<\/LI><\/OL><\/LI>Si la pièce jointe existe déjà sur le disque, elle ne sera pas téléchargée à nouveau<\/LI>Résumé des téléchargements (nombre total de téléchargements et taille totale des pièces jointes) fourni en sortie finale dans la console<\/LI><\/OL><\/P>Comment s'installer ?<\/STRONG><\/P><\/P>Suivez le guide
Notes :<\/STRONG><\/P><\/P>Mettez à jour la variable FeatureLayerId pour exécuter sur votre propre couche d'entités<\/LI>Spécifiez votre PortalUserName, PortalPassword si la couche d'entités est sécurisée<\/LI>Les noms des dossiers sont filtrés pour ne montrer que les caractères 0-9 et a-Z<\/LI>Ce script n'a pas été testé avec ArcGIS Enterprise (seulement les couches d'entités hébergées dans ArcGIS Online), mais il devrait quand même fonctionner<\/LI>Le code est également disponible via le
Hi Holly - have you ever got this script working before? I have not replicated this behaviour with the version of the Python API included with ArcGIS Pro 2.1.2 (v1.2.5). Can you test with the FeatureLayerId = '092d075f4b3a40f78cf1329b20b0d5e7' - this is a public layer so you should be able to access.
I initially wrote this using v1.2.0 of the ArcGIS API for Python - I have not tested it with later versions.
Hi Michael, thanks for this post...very useful!
I am trying to download a feature layer created by a Survey123 application that allows users to capture multiple photos per record. To do so we had to build it as a relationship. Here is the URL: https:[host]/portal/home/item.html?id=f0dcf24f2bb9440d9fa2c8ec64141fe1#data. When viewing the Data tab from the Portal I can select the "(0) Add" text in the "Photos and Files" field to access the photos. I tried plugging f0dcf24f2bb9440d9fa2c8ec64141fe1#data into the FeatureLayerID =, but it returns no files. Any idea what I may be missing?
Hi Eric,
If the photos are stored in a table with no spatial attributes, it will appear as a table through the API (as opposed to a layer). Therefore the 'layers' text in lines 81/82/96 have to be chanced to 'tables'.
<SPAN class="comment token">#Lines 81/82</SPAN> <SPAN class="keyword token">for</SPAN> i <SPAN class="keyword token">in</SPAN> range<SPAN class="punctuation token">(</SPAN>len<SPAN class="punctuation token">(</SPAN>itemObject<SPAN class="punctuation token">.</SPAN>tables<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN> featureLayer <SPAN class="operator token">=</SPAN> itemObject<SPAN class="punctuation token">.</SPAN>tables<SPAN class="punctuation token">[</SPAN>i<SPAN class="punctuation token">]</SPAN> <SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN> <SPAN class="comment token">#Line 96</SPAN> logger<SPAN class="punctuation token">.</SPAN>info<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'Currently looping through feature attachments in layer {} of {}: storing in folder named "{}"'</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">(</SPAN>str<SPAN class="punctuation token">(</SPAN>i <SPAN class="operator token">+</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> str<SPAN class="punctuation token">(</SPAN>len<SPAN class="punctuation token">(</SPAN>itemObject<SPAN class="punctuation token">.</SPAN>tables<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> featureLayerName<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
This should loop through any associated tables (as opposed to layers) and return the appropriate results.
Kind regards,
Mikie
That was exactly what I needed! Thanks so much!
Hey Mike,
The script is running as expected, but I am trying to get it to place the attachments in a folder named after a value specified by a specific field ("Subject") in the related layer. Do you have any pointers for how to configure that?
Thanks!
Eric
Hi Eric - a similar question came through in one of the comments above. You would have to alter the query to send back the appropriate attributes, and then use the returned attributes whilst specifying the associated file name.
I can't carry out this program because the following error happens. Please tell me the solution.
My feature layer comprises Japanese words. So this error may happen.
Is this program applicable to Japanese layers?
File "C:\ProgramData\Anaconda3\lib\http\client.py", line 1117, in putrequest self._output(request.encode('ascii'))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 43-45: ordinal not in range(128)
Hi Holly,
I was getting the same error as you. I fixed it by adding [0] to the end of the currentAttachmentPath variable so that it gets the first and only attachment path in that list and uses it. I had to add it to this part: renameFile(currentAttachmentPath[0], newAttachmentPath) under both GroupedFolder and IndividualFolder. I tested it on Michael's public layer, and was able to download the attachments.
Thank you very much.
I'll try it.
2018年7月6日(金) 3:08 Chelsea Rozek <geonet@esri.com>:
GeoNet <https://community.esri.com/?et=watches.email.document_comment>Downloading Feature Layer Attachments via the ArcGIS API for Pythonnew comment by Chelsea Rozek<https://community.esri.com/people/rozekc_washtenaw?et=watches.email.document_comment> Viewall comments on this document<https://community.esri.com/docs/DOC-10441-downloading-feature-layer-attachments?commentID=63017&et=watches.email.document_comment#comment-63017>
GeoNet <https://community.esri.com/?et=watches.email.document_comment>
Downloading Feature Layer Attachments via the ArcGIS API for Python
new comment by Chelsea Rozek
<https://community.esri.com/people/rozekc_washtenaw?et=watches.email.document_comment> View
all comments on this document
<https://community.esri.com/docs/DOC-10441-downloading-feature-layer-attachments?commentID=63017&et=watches.email.document_comment#comment-63017>
Hi 竹元 貴彦,
Did you get the script working with the pre-set public layer? I have not tested this against layers with Japanese. Does the layer itself (layer name, field names, etc.) contain Japanese, or is there Japanese only in the attributes? Is the layer available publically, or could you share it with me through a group? My ArcGIS Online username is mikie.kelly.
Mikie,
This is an awesome script and I was able to get it working quickly despite limited Python experience. I am wondering how line 92 might be modified to fetch GUIDs instead of OIDs.
I worked using private layer. So I can't show it imediately. I make or search another layer that contains Japanese to show you the problem as soon as possible.
My private layer has Japanese both field name and attributes.
Hi Bob,
Global ID's are returned with the attachments. You can access them by adding to lines 108-111 as below (both attachment Global ID and attachment parent Global ID accessible):
<SPAN class="keyword token">for</SPAN> k <SPAN class="keyword token">in</SPAN> range<SPAN class="punctuation token">(</SPAN>len<SPAN class="punctuation token">(</SPAN>currentObjectIdAttachments<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN> attachmentGlobalId <SPAN class="operator token">=</SPAN> currentObjectIdAttachments<SPAN class="punctuation token">[</SPAN>k<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'globalId'</SPAN><SPAN class="punctuation token">]</SPAN> attachmentParentGlobalId <SPAN class="operator token">=</SPAN> currentObjectIdAttachments<SPAN class="punctuation token">[</SPAN>k<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'parentGlobalId'</SPAN><SPAN class="punctuation token">]</SPAN> attachmentId <SPAN class="operator token">=</SPAN> currentObjectIdAttachments<SPAN class="punctuation token">[</SPAN>k<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'id'</SPAN><SPAN class="punctuation token">]</SPAN> attachmentName <SPAN class="operator token">=</SPAN> currentObjectIdAttachments<SPAN class="punctuation token">[</SPAN>k<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'name'</SPAN><SPAN class="punctuation token">]</SPAN> attachmentSize <SPAN class="operator token">=</SPAN> currentObjectIdAttachments<SPAN class="punctuation token">[</SPAN>k<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'size'</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
Then you can reference them when naming the file 117
fileName = '{}-{}-{}'.format(attachmentGlobalId, attachmentParentGlobalId, attachmentName)<SPAN class="line-numbers-rows"><SPAN></SPAN></SPAN>
Works like a charm. Thanks.
Great script. I would also appreciate having the attachment files named based on a field in the associated attribute table. You mentioned this is possible but would require some rejigging -- have you or anyone else developed script for this?
Annalise
Hi Annalise,
I'm afraid not, at least not that I'm aware of.
I work with the U.S. Fish and Wildlife Service and use an Enterprise Account login. Can this script function with an Enterprise login?
#What are your ArcGIS Enterprise/ArcGIS credentials? This is case sensitive.PortalUserName = 'email here'PortalPassword = 'password here'PortalUrl = 'https://fws.maps.arcgis.com'
I changed the PortalURL to direct to the FWS ArcGIS URL but I keep getting an error message -- [ERROR:arcgis._impl.connection:Invalid username or password.]
I've confirmed that the username and password I entered are correct.
https://fws.maps.arcgis.com is an address for ArcGIS as opposed to ArcGIS Enterprise. All ArcGIS accounts can simply use https://www.arcgis.com for the PortalUrl. An ArcGIS Enterprise address is likely to look like this: https://portalcomputer.domain.com/portal/home.
PortalUserName = 'MKelly@MyDomain'
PortalPassword = 'MyPassword'
PortalUrl = 'https://portalcomputer.domain.com/portal/home'
Further information can be found here. One thing to note is that the username can be case sensitive - you need to replicate how it appears in the Portal organisation page.
Hi Michael
Great script, quick question: Is it possible to get a file-like object from the attachments manager without downloading the file locally first?
So instead of downloading the file with attachments.download i would want to have the attachment as an in-memory object...
Currently my script first downloads the attachment and then opens it into a binary file-like object and then delete the local file when i am done....
Any help will be appreciated.
Hi Deon,
I haven't noticed anything like that in the AttachmentManager class - is there a problem with the approach you are currently taking?
get_list includes the URL of any attachments (from what I remember) if that's any use?
Hi Michael Kelly,
A couple of days ago our machines were upgraded to ArcGIS Pro 2.2.1 (Python 3.6.5).
Since then attachments fail to include "OBJECTID - ATTACHMENTID" in the attachment file names (only attachment names).
Can you think of the reason for this - from the top of your head? Otherwise, I'm more than happy to take a further look.
But I thought I'd ask you first.
Thanks,
Gee
All sorted.
It was the same error as what Holly Tran was experiencing. And managed to get it working by using Chelsea Rozek's suggestion.
Thanks guys,
Getting this error with enterprise. Anyone have a possible solution?
Sounds like Holly's error above, see my previous response for a solution: adding [0] to the end of the currentAttachmentPath variable
I did that step and this error popped up.
Is the feature layer you're running this on publicly accessible? If yes, what's the url?
this worked for me too (ie just adding [0] on line 123, and line 137, so that the line is
renameFile(currentAttachmentPath[0], newAttachmentPath)
instead of
renameFile(currentAttachmentPath, newAttachmentPath)
- I am running python 3.7.2, so it seems to be an issues that comes up when using a later version of python
Hello Michael Kelly. Thank you fo putting script together and sharing it with the Community. I forked your project in Github. I'm working on adding capabilities the Community is requesting here and will submit a commit soon. Plus I am adding the ability to choose a portal that is Enterprise or AGO, and some other automation.
As for saving each attachment with a file name using an attribute value I am running into an issue with the FeatureSet object that is returned from Query() I am passing in the currentObjectId to obtain the field containing the pole number, which is esupportstructure_facilityid.
currentObjectId <SPAN class="operator token">=</SPAN> featureObjectIds<SPAN class="punctuation token">[</SPAN><SPAN class="string token">'objectIds'</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">[</SPAN>j<SPAN class="punctuation token">]</SPAN> facilityID <SPAN class="operator token">=</SPAN> featureLayer<SPAN class="punctuation token">.</SPAN>query<SPAN class="punctuation token">(</SPAN>objectids<SPAN class="operator token">=</SPAN>currentObjectId<SPAN class="punctuation token">,</SPAN> outFields<SPAN class="operator token">=</SPAN><SPAN class="string token">'esupportstructure_facilityid'</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
The documentation appears to return a JSON object so I also tried treated it as such. When I do that another error appears as attached below,
FeatureSet' object is not subscriptable
I have not found any ArcGIS Python API examples that work with FeatureSet object and Query(), can you point to some samples or provide a snippet.
---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-1-bc6c83cbc278> in <module> 202 currentObjectId = featureObjectIds['objectIds'][j] 203 facilityID = featureLayer.query(objectids=currentObjectId, outFields='esupportstructure_facilityid')--> 204 poleNumber = facilityID['eSupportStructure_FACILITYID'] 205 logger.info(f'{poleNumber}') 206 currentObjectIdAttachments = featureLayer.attachments.get_list(oid=currentObjectId)TypeError: 'FeatureSet' object is not subscriptable
JSON Response from Query().
INFO:__main__:{"features": [{"geometry": {"x": 2678790.012921214, "y": 236090.84788563848}, "attributes": {"eSupportStructure_FACILITYID": "47185"}}], "objectIdFieldName": "OBJECTID", "globalIdFieldName": "GlobalID", "spatialReference": {"wkid": 102660, "latestWkid": 2238}, "geometryType": "esriGeometryPoint", "fields": [{"name": "eSupportStructure_FACILITYID", "alias": "Pole Number", "type": "esriFieldTypeString", "length": 20}]}
Michael Kelly Thanks for this script! I'm using it for a few applications and it works great! Recently I implemented a survey123 app that captures pictures in a repeat, and so images are attached to a related table. Can this script be modified to download attachments from related tables, or is does it need to be from the feature layer?
Hi, everybody.I followed the post to download the attached photos from a survey I generated (the photos are inside a table).I have also modified the script because I had the same problem as @Chelsea Rozek
I'm running the script inside the ArcGis Online Notebook.It doesn't give me any error and it seems to download the content. However I go to the path where the downloaded photos are supposed to be but the folder doesn't exist. Although the script is saying that the photos are downloaded.
INFO:__main__:Time: 2020-06-08 10:44:57.796204 INFO:__main__:Currently looping through feature attachments in layer 1 of 2: storing in folder named "0-foto" INFO:__main__:There are 386 features to iterate in this layer INFO:__main__:The size of the current attachment being downloaded is 1.17647MB ----------
INFO:__main__:C:\ScriptDownloads\\0-foto\\103/fotos-20200525-132307.jpg being renamed as C:\ScriptDownloads\\0-foto\\103\103-fotos-20200525-132307.jpg INFO:__main__:The size of the current attachment being downloaded is 1.69949MB
Any suggestions? Thanks
Les membres connectés peuvent publier, suivre les mises à jour, et plus encore. Nouveau ici ? Inscrivez-vous gratuitement.
Find useful guides, FAQs, and documents to help you navigate and make the most of Esri Community.