Let's look at POSIX FileID's. 0 is standard input, 1 is standard output, 2 is standard error. But that is only how it is at the start of your program. You can close FileIDs, you can reopen them (e.g. writing your stdout to a file), you can open further streams. Somehow the OS is keeping track of all this. Obviously at some place, there is a data structure with all the necessary information on a given stream in it. Since you can have many files, there's an array of those data structures somewhere. The FileID is an index into that array (to which you don't have any access, but which is helpful for you and the system to communicate with each other).
The C standard library's FILE * works a little differently, because what you get from fopen() is not an integer (being an index into an array), but the data structure holding the necessary information itself. My own C standard library holds in FILE the OS file handle (in case of POSIX, the FileID we had just been talking about), a pointer to the stream's buffer, counters for buffer size, buffer index, and buffer end (used for read buffers) as well as the position in the file, an array of ungetc()'ed characters, a counter of those characters, an integer of status flags, a mutex for accessing the file and structure, the file name, and a pointer to the next FILE structure as I administer them as a linked list.
But the nice thing is, you still don't have to bother with any of that. This structure is not part of the API. What you get is an opaque FILE * from fopen() which you then pass around to fread() and ftell() and fprintf() and whatnot, untill you fclose() the stream again. You never bother with what that FILE * actually points to. It's a
handle.
It doesn't have to be files. It doesn't have to be C. If you work with any object-oriented programming (OOP), an object instance is similarly a handle. You retrieve an instance of class MyStorage, let's call it ms, and now you can ms->read() or ms->write() or ms->delete(). But you never actually bother with what is behind ms itself. At least, as long as you are a well-behaved OO programmer.

Here, ms is a
handle to an instance of MyStorage.
And even if you are not using an OOP
language, as soon as you are using the pattern of "get a handle from one function, use the handle with several other functions, release the handle with one function",
that is basically object-oriented programming. Regardless whether you are looking at FILE * from the standard library, Image * from ImageMagic, MyClass x in C++ (or Java, or C#, or ...), all of that is a handle. (Indeed it is very easy to "wrap" a OOP API for a non-OOP programming language. The constructor becomes the function creating the handle, the destructor becomes the function releasing the handle, and all the classes' member functions become functions
using that handle. If you feel really ambitious, you can even recreate class inheritance using nothing but C structs as handles. AmigaOS Exec did that back in the 80's...
A handle is a piece of information that allows you to take the thing you identified in some earlier call, and then
work on it in some way. It's just a (very) convenient programming pattern.