Reputation: 244
I'm creating a Dropzone component.
I would like to make my <div>
red when the user tries to drag and drop an invalid file type.
Currently through dragstart
event I can get the MimeType of the file, but I can't get the file name or extension.
The accept property of the Input type=file allows filtering files either by MimeType (example: image/png) or by extension (example: .png). I wish my Dropzone could filter both by MimeType and by extension, just like Input type=file.
Upvotes: 1
Views: 1796
Reputation: 143
So, keep in mind that the extension is not actually something you can or need to validate on. The raw file data is simply the bytes whereas the extension is used by the operating system to try and pick the correct application to present that set of bytes.
Also, the accept
value for the input
element doesn't actually limit the file types which can be entered into the field. HTML attribute: accept - HTML: HyperText Markup Language | MDN
The accept attribute doesn't validate the types of the selected files; it provides hints for browsers to guide users towards selecting the correct file types. It is still possible (in most cases) for users to toggle an option in the file chooser that makes it possible to override this and select any file they wish, and then choose incorrect file types.
What I'm reading is that you're looking for validating that the document they are trying to drag into your dropzone is the correct file type which can only be done based on the mime-type
of the document itself which is possible and should be performed by both your front-end and backend applications. Common MIME types - HTTP | MDN
In the DragEvent
under dataTransfer.items
, you can convert that to an array using Array.from
then iterate over each item, you have exposed the item.kind
and item.type
.
item.kind
is either file
or text
item.type
when the kind
is file
this will give you a hint to the mime-type
which the user is attempting to drag into your dropzone.You would need to write a parser that would be able to take your accept
-mime-type and validate this value.
I've mocked up something which could help accomplish this for you.
Keep in mind:
onDrop
using a similar method and also onChange
if using an <input type="file" />
for manual uploads// this is your original accept tag
// which is a comma-separated list of types
const accept = "image/*";
// Just do this step once as the event fires pretty often
const acceptArr = accept
.split(",") // get the individual values
.map(a => {
// each value is now "*/*" (or the extension - not recommended)
// split the value further into the array parts
// if you are using extension, you could reference common extensions and convert them to a mime-type prior to doing the next step
return a.split("/");
});
// The resulting array would look something like this
/*
[
[ "image", "*" ], // <- from image/*
[ "application", "pdf"], // <- from application/pdf
[ ".doc", "" ] // <- if you used extensions - this cannot be validated, but you could potentially create your own map of extensions to mime-type
]
*/
/*
* @param e {DragEvent} the original drag event (this is React.DragEvent<HTMLDivElement> in typescript/react
*/
function onDragOver(e) {
if (e.dataTransfer?.items) {
const valid =
// the "accept" value was not supplied
!accept ||
// the "accept" value was a wild card
accept === "*/*" ||
accept === "*" ||
// the "dataTransfer.items" is not iterable in older ES versions
// therefore, we need to convert it into an Array
// Thankfully since it has a "length" property, we can use Array.from
Array
.from(e.dataTransfer.items)
// every item must return "true"
.every(item => {
const { kind, type } = item;
if (kind === "file") {
// the type is */* format
const [ namespace, friendlyName ] = type.split("/");
// now iterate over the accepted file types
return acceptArr
.every(accepted => {
const [ acceptedNs, acceptedName ] = accepted;
return
(acceptedNs === "*" || acceptedNs === namespace) &&
(acceptedName === "*" || acceptedName === friendlyName)
});
} else {
return false; // not a file
}
});
}
}
Upvotes: 2