Thank to @Rik for pointers. Here is some code which does this using filepond in Vue. Feels slightly hacky but does the job.
Accept the image/heic content type and add a custom validator:
<file-pond
v-if="supported"
ref="pond"
name="photo"
:allow-multiple="multiple"
accepted-file-types="image/jpeg, image/png, image/gif, image/jpg, image/heic"
:file-validate-type-detect-type="validateType"
:files="myFiles"
image-resize-target-width="800"
image-resize-target-height="800"
image-crop-aspect-ratio="1"
label-idle="Drag & Drop photos or <span class="btn btn-white ction"> Browse </span>"
:server="{ process, revert, restore, load, fetch }"
@init="photoInit"
@processfile="processed"
@processfiles="allProcessed"
/>
Then in the validator check for the filename, to handle browsers that don't set the correct MIME type:
validateType(source, type) {
const p = new Promise((resolve, reject) => {
if (source.name.toLowerCase().indexOf('.heic') !== -1) {
resolve('image/heic')
} else {
resolve(type)
}
})
return p
}
Then in the process callback, spot the HEIC file and use heic2any to convert it to PNG and use that data in the upload.
async process(fieldName, file, metadata, load, error, progress, abort) {
await this.$store.dispatch('compose/setUploading', true)
const data = new FormData()
const fn = file.name.toLowerCase()
if (fn.indexOf('.heic') !== -1) {
const blob = file.slice(0, file.size, 'image/heic')
const png = await heic2any({ blob })
data.append('photo', png, 'photo')
} else {
data.append('photo', file, 'photo')
}
data.append(this.imgflag, true)
data.append('imgtype', this.imgtype)
data.append('ocr', this.ocr)
data.append('identify', this.identify)
if (this.msgid) {
data.append('msgid', this.msgid)
} else if (this.groupid) {
data.append('groupid', this.groupid)
}
const ret = await this.$axios.post(process.env.API + '/image', data, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUpLoadProgress: e => {
progress(e.lengthComputable, e.loaded, e.total)
}
})
if (ret.status === 200 && ret.data.ret === 0) {
this.imageid = ret.data.id
this.imagethumb = ret.data.paththumb
this.image = ret.data.path
if (this.ocr) {
this.ocred = ret.data.ocr
}
if (this.identify) {
this.identified = ret.data.items
}
load(ret.data.id)
} else {
error(
ret.status === 200 ? ret.data.status : 'Network error ' + ret.status
)
}
return {
abort: () => {
// We don't need to do anything - the server will tidy up hanging images.
abort()
}
}
},
The server is then blissfully unaware that the original file was HEIC at all, and everything proceeds as normal.