Attachment failing to upload

1422
3
Jump to solution
12-03-2019 04:09 PM
CoryDavis
New Contributor III

My app allows the user to create a point feature and attach multiple photos to it. The photos are displayed in a custom callout which appears when the user taps the feature. The point is created and the attachments are added to it successfully; I know because I can see the photos when I select the point on the map and no client-side errors are thrown. However, the attachments are gone the next time I open the app. The point appears and I can see it in ArcGIS Pro on the back end, but there are no attachments in the associated table.

This error is logged in the Server Manager: "Unable to complete upload operation, File size or type not supported for this service". The photos are less than 2 MB each and I have never uploaded more than 3 at a time.

I know attachments are enabled for the layer because I successfully added this feature with a photo via ArcGIS Pro and it saved to the database. This leads me to believe the failure is either on the front end or perhaps a setting the attachment is violating, but I don't know how to check that.

Here are the steps to create the feature and any photo attachments:

1. The user presses a button which transitions to a new screen where they select photos.

2. The user selects any number of photos from the gallery or takes new ones with the camera. This is done using UIImagePickerController.

3. The user presses "done". The app prepares to transition to a view controller where the point attributes are set. The point is created in the "prepare" function.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "create-photo-point-segue" {
            let nc = segue.destination as! NewFeatureNavigationController
            let vc = nc.topViewController as! CreateFeatureViewController
            let template: AGSFeatureTemplate? = (photoFeatureLayer?.featureTable as? AGSServiceFeatureTable)?.featureTemplates[0]
            vc.template = template
            
            //Create the new photo point feature with the photo attachments
            let newAgsFeature = (photoFeatureLayer?.featureTable as? AGSServiceFeatureTable)?.createFeature(with: template!)
            newAgsFeature!.attributes.setValue(primaryLocation?.latitude, forKey: "LATITUDE")
            newAgsFeature!.attributes.setValue(primaryLocation?.longitude, forKey: "LONGITUDE")
            newAgsFeature!.attributes.setValue(primaryDate, forKey: "TIMESTAMP")
            let point = AGSPoint(x: primaryLocation!.longitude , y: primaryLocation!.latitude, spatialReference: .wgs84())
            newAgsFeature!.geometry = point
            attachTxtDocs(feature: newAgsFeature!, photoArray: photoSelectionData)
            
            vc.feature = newAgsFeature
            vc.featureAttachments = photoSelectionData
            vc.hasAttachments = true
            vc.layer = photoFeatureLayer!
            nc.collectionPoint = (self.navigationController as! AddPhotoFeatureNavigationController).collectionPoint
            let mapVC = (self.navigationController as! AddPhotoFeatureNavigationController).mapVC
            mapVC?.navController = nc
            nc.mapVC = mapVC
            nc.intermediateVC = self
        }
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

3a. The photos are serialized to txt documents (necessary to capture metadata) and attached to the newly-created point. I'm aware that because the attachment is a text document that could be why it doesn't save, but I doubt it because I have an alternate function which does not serialize the photo and it experiences the same problem.

func attachTxtDocs(feature:AGSArcGISFeature, photoArray:[[UIImagePickerController.InfoKey:Any]]) {
        var i = 0
        var tempFileUrls: [URL] = []
        DispatchQueue.main.async {
           SVProgressHUD.show(withStatus: "Attaching Photos...")
        }
        for photo in photoArray {
            var type: String
            if let typeOptional = (photo[UIImagePickerController.InfoKey.imageURL] as! URL?) {
                let urlTokens = typeOptional.absoluteString.split(separator: ".")
                type = urlTokens[urlTokens.count - 1].description
            }
            else {
                type = "jpg"
            }
            var imageData: Data?
            switch type {
            case "jpg", "jpeg":
                imageData = (photo[UIImagePickerController.InfoKey.originalImage] as! UIImage).jpegData(compressionQuality: 0.5)!
            case "png":
                imageData = (photo[UIImagePickerController.InfoKey.originalImage] as! UIImage).pngData()!
            default:
                print("Invalid type")
            }
            guard imageData != nil else {
                SVProgressHUD.dismiss()
                return
            }
            let fileName = serializePhoto(lat: String(geolocations[i].latitude),
                                          lon: String(geolocations[i].longitude),
                                          timestamp: timestamps[i].description,
                                          fileType: type,
                                          photoData: imageData!)
            let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName).appendingPathExtension("txt")
            tempFileUrls.append(fileUrl)
            do {
                let photoData = try Data(contentsOf: fileUrl)
                feature.addAttachment(withName: "attachment" + String(i),
                                  contentType: "txt",
                                  data: photoData) { (attachment, error) in
                    SVProgressHUD.dismiss()
                    if let error = error {
                        print(error.localizedDescription)
                    }
                    if attachment != nil {
                        print("Photo successfully attached.")
                        self.removeTempDoc(fileUrl: fileUrl)
                    }
                }
            }
            catch {
                print("Failed to fetch photo data from " + fileUrl.absoluteString)
                return
            }
            i += 1
        }
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

4. The feature's attributes are set and is loaded after the transition to the final view.

5. The user confirms the attributes are correct and hits "save". The edits are applied to the table.

    func applyEdits(to table:AGSServiceFeatureTable) {
        SVProgressHUD.setDefaultMaskType(.black)
        DispatchQueue.main.async {
            SVProgressHUD.show(withStatus: "Saving")
        }
        table.applyEdits(completion: { (results, error) in
            SVProgressHUD.dismiss()
            if let error = error {
                print(error)
                let alert = UIAlertController(title: "Alert!", message: "Unable to create feature: \(error.localizedDescription)", preferredStyle: .alert)
                let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
                alert.addAction(okAction)
                self.present(alert, animated: true, completion: nil)
                return
            }
            if self.referenceFeature != nil {
                self.deleteReferenceFeature()
            } else {
                self.navigationController?.dismiss(animated: true, completion: nil)
            }
        })
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This app uses ArcGIS Runtime v100.6. The back end is ArcServer 10.7.1.

Can someone offer a solution?

0 Kudos
1 Solution

Accepted Solutions
Nicholas-Furness
Esri Regular Contributor

Hi Cory,

Try adding a filetype suffix to the filename. Right now you're ‌naming them attachment0, attachment1, attachment2 etc.

Can you try naming them attachment0.txt, attachment1.txt, etc.?

And as Michael suggested, I would use EXIF data and store them as images. That way they'll be easily accessible to other components of the ArcGIS platform.

Nick

View solution in original post

3 Replies
MichaelDavis3
Occasional Contributor III

I'll preface this by saying that we experienced endless frustration with attachments in our own apps and have pretty much abandoned the use of them entirely in favor of an alternate upload workflow combined with a standalone database table for documenting photos and associated data.  

That said I'd try a couple of things:

  1. Up the max upload size on your server - I know you said the photos were only 2mb but was that before they were serialized?  Guessing the files are getting a lot larger when you do that.
  2. Perhaps consider writing the metadata to as EXIF data rather than using the serialized txt approach.  That is exactly what EXIF data is for...  
0 Kudos
Nicholas-Furness
Esri Regular Contributor

Hi Cory,

Try adding a filetype suffix to the filename. Right now you're ‌naming them attachment0, attachment1, attachment2 etc.

Can you try naming them attachment0.txt, attachment1.txt, etc.?

And as Michael suggested, I would use EXIF data and store them as images. That way they'll be easily accessible to other components of the ArcGIS platform.

Nick

CoryDavis
New Contributor III

The missing extension turned out to be the source of the error. Thanks so much!

0 Kudos