Well... I'm out of ideas on what to write about. Do you have any ideas? Please, write me in any comment section you find, and I will do my best to tell you about that particular subject

Step 14 – Crop and resize uploaded images in CodeIgniter

Created at: December 18, 2014; Last update: January 23, 2015

(created at: December 18, 2014; last update: December 18, 2014)
Being part of what is actually a larger tutorial, this step will assume that you’ve uploaded one or more image files through a form. So in this tutorial, which continues the previous step – Uploading multiple files (images) in CodeIgniter – I will assume that you’ve used a multiple attribute on the file input element. Also, I will assume that the array you will have after uploading the file(s) will have a structure just like the one in the image below.

Now, if you agree with the prelude of this tutorial, we can get to work. You can of course get to work even if you didn’t use multiple attribute on the file input element, but take care about accessing the array returned by the upload library.

We will continue to work in the same controller that we’ve built in the previous tutorial:

Welcome.php

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

<?phpdefined('BASEPATH')ORexit('No direct script access allowed');

classWelcomeextendsCI_Controller{

private$_uploaded;

publicfunctionindex()

{

$this->load->helper('form');

$data['title']='Multiple file upload';

// let's consider that the form would come with more fields than just the files to be uploaded. If this is the case, we would need to do some sort of validation. If we are talking about images, the only method of validation for us would be to put the upload process inside a validation callback;

// from here on you can do whatever you wish with the uploaded data or the other form fields that you might have. I decided to exit here, since this is not the object of our tutorial.

exit;

}

}

$this->load->view('upload_form',$data);

}

// now the callback validation that deals with the upload of files

publicfunctionfileupload_check()

{

// we retrieve the number of files that were uploaded

$number_of_files=sizeof($_FILES['uploadedimages']['tmp_name']);

// considering that do_upload() accepts single files, we will have to do a small hack so that we can upload multiple files. For this we will have to keep the data of uploaded files in a variable, and redo the $_FILE.

First thing first – What do we want to do with the uploaded images

Before we get to write the code, we must make sure we know what we want to do with the images we’ve uploaded. So allow me to share with you a real world scenario:

Since we’ve uploaded more than one file (image), I will suppose that this is for a photo gallery. So, we want the images to have a fixed width and height. This way we make sure the images of the photo gallery won’t break the site’s design. Also, when doing the cropping we want the cropped image to be taken from the center of the source image. We also want a thumbnail of the image with the same characteristics – a fixed width and height, center of the source image.

Let’s suppose our gallery will have images that are of of 620px by 400px. Also, the thumbnails will have to be 100px by 100px. Being thumbnails of the bigger images, we will append to the file name a ‘_thumb’ string.

Getting to work

To keep things tidy from the start we will do the image manipulation inside a separate method. As we don’t want this method to be accessible from the browser we will make it private, and we will call it from the method that is in direct connection with the user (in this case the index() method we’ve created in the previous step).

Let’s name the private method _image_creation(). The method will receive the array with the data regarding one source image (NOT THE ARRAY WE CREATED IN THE PREVIOUS STEP). This way, we will make sure that the method can be used even if the file input element doesn’t have the ‘multiple’ attribute (that is, if we have a form that doesn’t do a multiple file upload). This means that the method will receive an array that looks something like this:

INI

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

Array

(

[file_name]=>mypic.jpg

[file_type]=>image/jpeg

[file_path]=>/path/to/your/upload/

[full_path]=>/path/to/your/upload/jpg.jpg

[raw_name]=>mypic

[orig_name]=>mypic.jpg

[client_name]=>mypic.jpg

[file_ext]=>.jpg

[file_size]=>22.2

[is_image]=>1

[image_width]=>800

[image_height]=>600

[image_type]=>jpeg

[image_size_str]=>width="800"height="200"

)

The private method will return an array with the images that were created. This way, we can use the new images’ names if we want to insert them in some sort of database:

_image_creation()

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

privatefunction_image_creation($image)

{

// we make sure we receive an array. if no array is given or the array is empty, return false

if(!is_array($image)||empty($image))

{

returnFALSE;

}

// also let's make sure IT IS an image

if($image['is_image']!=1)

{

returnFALSE;

}

//let's have an array to return

$new_images=array();

//we return the array with the new images

return$new_images;

}

The manipulation of the images will be done only after we’ve made sure the form validation ended with success and the images were uploaded. So, let’s modify that part of the method, so that instead of outputting the array it will output an array which will be received from the private method we’ve talked about before:

index()

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

publicfunctionindex()

{

$this->load->helper('form');

$data['title']='Multiple file upload';

// let's consider that the form would come with more fields than just the files to be uploaded. If this is the case, we would need to do some sort of validation. If we are talking about images, the only method of validation for us would be to put the upload process inside a validation callback;

// let's store the new created images' data inside an array for later use

$created_images=array();

foreach($this->_uploaded as$key=>$source_image)

{

//from each source image we will create two images, the two images' data will be stored as an array for the source image's key

$new_images=$this->_image_creation($source_image);

$created_images[$key]=$new_images;

}

// now let's verify the new images have been created

echo'<pre>';

print_r($created_images);

echo'</pre>';

exit;

}

}

$this->load->view('upload_form',$data);

}

Of course, you can test the return of the arrays by uploading one or more images, but for the moment you will just receive an empty array.

Now let’s turn to our _image_creation() method. First of all let’s make sure we have all the data: the width an height of the image and of the thumbnail, and what string will be appended to the thumbnail’s file name:

_image_creation()

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

privatefunction_image_creation($image)

{

// we make sure we receive an array. if no array is given or the array is empty, return false

if(!is_array($image)||empty($image))

{

returnFALSE;

}

// also let's make sure IT IS an image

if($image['is_image']!=1)

{

returnFALSE;

}

//let's have an array to return

$new_images=array();

$image_width=620;

$image_height=200;

$thumb_width=100;

$thumb_height=100;

$thumb_name='-thumb';

// let's put the gallery images and thumbnails in a different directory (which will be public, of course... and writable) - make sure the directory exists

$gallery_path=FCPATH.'media/gallery/';

//we return the array with the new images

return$new_images;

}

We load the library and set the first configurations:

PHP

1

2

3

4

5

6

7

8

// load the library

$this->load->library('image_lib');

// we set the image library that we want to be used

$config['image_library']='gd2';

// we will take the source image from the $image array having the same source for the new image and the new thumbnail, we set the source here. of course you could use the new image as source for the thumbnail image, but after you've created the new image

$config['source_image']=$image['full_path'];

// we set maintain_ratio to FALSE because we want do do a crop for the images

$config['maintain_ratio']=FALSE;

Now, the tedious part. Calculating the width and height and also from where the crop will be made. I won’t explain you a lot here, because it has to do with the math of cropping and not with the image manipulation:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//calculate the source image's ratio

$source_ratio=$image['image_width']/$image['image_height'];

//calculate the ratio of the new image

$new_ratio=$image_width/$image_height;

//if the source image's ratio is not the same with the new image's ratio, then we do the cropping. else we just do a resize

if($source_ratio!=$new_ratio)

{

// if the new image' ratio is bigger than the source image's ratio or the new image is a square and the source image's height is bigger than it's width, we will take source's width as the width of the image

if($new_ratio>$source_ratio||(($new_ratio==1)&&($source_ratio<1)))

{

$config['width']=$image['image_width'];

$config['height']=round($image['image_width']/$new_ratio);

// now we will tell the library to crop from a certain y axis coordinate so that the new image is taken from the vertical center of the source image

If after this, you still have some questions about how I did the calculation, I think it would be best for you to look at a great tutorial (if not all tutorials…) that can be found on CodeIgniter.tv: Create multiple aspect ratio thumbnails

Now what about the image name? We decided to put the new processed images inside the media/gallery directory, but what if a file with the name of the image we’ve just uploaded already exists? Then we must try a different name. So why not loop through possible names until we find a filename that doesn’t exist:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// how will we name the image? and what if the image name already exists in the gallery?

// we will give it 100 tries. if after 100 tries it can't find a suitable name, then the problem is your imagination in naming the files that you've uploaded

for($i=1;$i<=100;$i++)

{

$new_file=$image['raw_name'].'-'.$i.$image['file_ext'];

$new_thumb=$image['raw_name'].'-'.$i.$thumb_name.$image['file_ext'];

if(!file_exists($new_file))

{

$image_path=$gallery_path.$new_file;

$thumb_path=$gallery_path.$new_thumb;

}

}

}

$config['new_image']=$image_path;

Being a crop of image, which will be followed by a resize, we can set the quality of the new image to 100%. Why 100%? Because we don’t want that the quality of the new image to be 70% (from quality of the resize) from 70% (from the quality of the crop). I sure hope you understood this sentence…

PHP

1

2

// for cropping we want 100% image quality

$config['quality']='100%';

After we’ve set up the configuration is time to do the cropping and resizing and keep the eventual errors and the new file names and paths inside the array that will be returned from the method we’ve created:

Now, let’s create the thumbnail… We do this almost the same way we’ve created the image:

the thumbnail

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

// PROCESS THE THUMBNAIL

// let's reset the errors

$errors=array();

// now we will do a reset for some of the $config array

$config['source_image']=$config['new_image'];

//calculate the source image's ratio

$source_ratio=$image['image_width']/$image['image_height'];

//calculate the ratio of the new image

$new_ratio=$thumb_width/$thumb_height;

//if the source image's ratio is not the same with the thumbnail image's ratio, then we do the cropping. else we just do a resize

if($source_ratio!=$new_ratio)

{

// if the new image' ratio is bigger than the source image's ratio or the new image is a square and the source image's height is bigger than it's width, we will take source's width as the width of the image

if($new_ratio>$source_ratio||(($new_ratio==1)&&($source_ratio<1)))

{

$config['width']=$image['image_width'];

$config['height']=round($image['image_width']/$new_ratio);

// now we will tell the library to crop from a certain y axis coordinate so that the new image is taken from the vertical center of the source image

Now is the time for everyone to jump over my head and cry “BLASPHEMY! YOU DIDN’T RESPECT THE ‘DO NOT REPEAT YOURSELF’ RUUUUULE!!!”. Yes, I did repeat myself, by writing the same code twice (when calculating the sizes and doing the cropping and resizing), didn’t I? But I did show you how you can clean your code in previous tutorials. Why don’t you try to clean this code for a change?

Here is the final code for this and for the previous tutorial… and after that, if you think you are unable to clean the code, and you pay me with a beer, I will help you clean the code:

Welcome.php

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

<?phpdefined('BASEPATH')ORexit('No direct script access allowed');

classWelcomeextendsCI_Controller{

private$_uploaded;

publicfunctionindex()

{

$this->load->helper('form');

$data['title']='Multiple file upload';

// let's consider that the form would come with more fields than just the files to be uploaded. If this is the case, we would need to do some sort of validation. If we are talking about images, the only method of validation for us would be to put the upload process inside a validation callback;

// let's store the new created images' data inside an array for later use

$created_images=array();

foreach($this->_uploaded as$key=>$source_image)

{

//from each source image we will create two images, the two images' data will be stored as an array for the source image's key

$new_images=$this->_image_creation($source_image);

$created_images[$key]=$new_images;

}

// now let's verify the new images have been created. of course, as always don't do this at home. always output in a view file

echo'<pre>';

print_r($created_images);

echo'</pre>';

exit;

}

}

$this->load->view('upload_form',$data);

}

publicfunctionfileupload_check()

{

// retrieve the number of images uploaded;

$number_of_files=sizeof($_FILES['uploadedimages']['tmp_name']);

// considering that do_upload() accepts single files, we will have to do a small hack so that we can upload multiple files. For this we will have to keep the data of uploaded files in a variable, and redo the $_FILE.

// we make sure we receive an array. if no array is given, return false

if(!is_array($image)||empty($image))

{

returnFALSE;

}

// also let's make sure IT IS an image

if($image['is_image']!=1)

{

returnFALSE;

}

//let's have an array to return

$new_images=array();

//some parameters

$image_width=620;

$image_height=200;

$thumb_width=100;

$thumb_height=100;

$thumb_name='-thumb';

// let's put the gallery images and thumbnails in a different directory (which will be public, of course... and writable)

$gallery_path=FCPATH.'media/galleries/';

// PROCESS THE MAIN IMAGE

// load the library

$this->load->library('image_lib');

// let's be prepared for any errors we may encounter

$errors=array();

// we set the image library that we want to be used

$config['image_library']='gd2';

// we will take the source image from the $image array having the same source for the new image and the new thumbnail, we set the source here. of course you could use the new image as source for the thumbnail image, but after you've created the new image

$config['source_image']=$image['full_path'];

// we set maintain_ratio to FALSE because we want do do a crop for the images

$config['maintain_ratio']=FALSE;

//calculate the source image's ratio

$source_ratio=$image['image_width']/$image['image_height'];

//calculate the ratio of the new image

$new_ratio=$image_width/$image_height;

//if the source image's ratio is not the same with the new image's ratio, then we do the cropping. else we just do a resize

if($source_ratio!=$new_ratio)

{

// if the new image' ratio is bigger than the source image's ratio or the new image is a square and the source image's height is bigger than it's width, we will take source's width as the width of the image

if($new_ratio>$source_ratio||(($new_ratio==1)&&($source_ratio<1)))

{

$config['width']=$image['image_width'];

$config['height']=round($image['image_width']/$new_ratio);

// now we will tell the library to crop from a certain y axis coordinate so that the new image is taken from the vertical center of the source image

//if the source image's ratio is not the same with the thumbnail image's ratio, then we do the cropping. else we just do a resize

if($source_ratio!=$new_ratio)

{

// if the new image' ratio is bigger than the source image's ratio or the new image is a square and the source image's height is bigger than it's width, we will take source's width as the width of the image

if($new_ratio>$source_ratio||(($new_ratio==1)&&($source_ratio<1)))

{

$config['width']=$image['image_width'];

$config['height']=round($image['image_width']/$new_ratio);

// now we will tell the library to crop from a certain y axis coordinate so that the new image is taken from the vertical center of the source image