The memory layout of a (2D) array texture is pretty similar to a 3D texture, with the individual image layers stored one after the other in a big 3D-array of pixels. So that's also how you set them. You either load each individual image into a big
GLubyte array one after the other and set it with
glTex(Sub)Image3D or you load each image into its own
GLubyte array and transmit that to the texture with
glTexSubImage3D one after the other (or any other more complex process involving buffers or whatever, but you get the idea).
So much to the theory. The problem here is the part where you say
However there's no guarantee that the textures are the same dimensions.
This contradicts with how array textures work. As the corresponding documentation you link to says:
An Array Texture is a Texture where each mipmap level contains an array of images of the same size.
So the individual layers of an array texture have to be the same size. Simplifyingly said, an array texture is really just a 3D texture without filtering along the z-axis. So you can't get different-sized textures into such a regular box of pixels.
There's a few options you have here, though:
You could scale every image to a common size, depending on how different they really are and what rescaling means to your quality and/or memory demands.
You could just drop using array textures and implement whatever you want to do with normal 2D textures and a likely more complicated process on the CPU side.
You could use Bindless Textures. This is a rather modern non-core feature (only usable through extensions yet) that is really flexible and allows you to access textures in a shader by using a unique GPU handle (a 64-bit ID, so to say). This allows you to use whatever and how many textures you want in a shader, depending on some arbitrarily input value (even a vertex attribute).
In this case you wouldn't just a have access to a continuous array of N textures, rather than the wealth of all the textures you have allocated. You would just use normal independent 2D textures and use their respective texture handles rather than an array index. Of course this comes with a few limitations and intricacies, too. But more about that in the corresponding documentation.