업로드하기 전에 HTML5를 사용하여 이미지 크기 조정


102

이 질문에 답하는 stackoverflow에 대한 몇 가지 다른 게시물과 질문을 발견했습니다. 기본적 으로이 게시물 과 동일한 것을 구현하고 있습니다.

그래서 여기에 내 문제가 있습니다. 사진을 업로드 할 때 나머지 양식도 제출해야합니다. 내 HTML은 다음과 같습니다.

<form id="uploadImageForm" enctype="multipart/form-data">
  <input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
  <input id="name" value="#{name}" />
  ... a few more inputs ... 
</form>

이전에는 이미지 크기를 조정할 필요가 없었으므로 내 자바 스크립트는 다음과 같이 보입니다.

window.uploadPhotos = function(url){
    var data = new FormData($("form[id*='uploadImageForm']")[0]);

    $.ajax({
        url: url,
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
            ... handle error...
            }
        }
    });
};

이 모든 것이 훌륭하게 작동했습니다 ... 이제 이미지 크기를 조정해야합니다. 업로드 된 이미지가 아닌 크기가 조정 된 이미지가 게시되도록 양식의 이미지를 어떻게 바꿀 수 있습니까?

window.uploadPhotos = function(url){

    var resizedImage;

    // Read in file
    var file = event.target.files[0];

    // Ensure it's an image
    if(file.type.match(/image.*/)) {
        console.log('An image has been loaded');

        // Load the image
        var reader = new FileReader();
        reader.onload = function (readerEvent) {
            var image = new Image();
            image.onload = function (imageEvent) {

                // Resize the image
                var canvas = document.createElement('canvas'),
                    max_size = 1200,
                    width = image.width,
                    height = image.height;
                if (width > height) {
                    if (width > max_size) {
                        height *= max_size / width;
                        width = max_size;
                    }
                } else {
                    if (height > max_size) {
                        width *= max_size / height;
                        height = max_size;
                    }
                }
                canvas.width = width;
                canvas.height = height;
                canvas.getContext('2d').drawImage(image, 0, 0, width, height);
                resizedImage = canvas.toDataURL('image/jpeg');
            }
            image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
    }


   // TODO: Need some logic here to switch out which photo is being posted...

    var data = new FormData($("form[id*='uploadImageForm']")[0]);

    $.ajax({
        url: url,
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
            ... handle error...
            }
        }
    });
};

파일 입력을 양식에서 옮기고 값을 크기가 조정 된 이미지의 값으로 설정 한 양식에 숨겨진 입력을 갖는 것에 대해 생각했습니다 ...하지만 이미지를 대체 할 수 있는지 이미 양식에 있습니다.


서버 측 언어로 작업하고 있습니까? 아니면 html5 및 javascript 만 사용합니까?
luke2012 2014 년

1
@ luke2012 자바 서버 측
ferics2 2014 년

jCrop과 같은 것을 사용하여 클라이언트 측에서 이미지를 자른 다음 좌표를 서버 측으로 보내고 자르십시오. 즉BufferedImage dest = src.getSubimage(rect.x, rect.y, rect.width, rect.height);
luke2012 2014 년

4
@ luke2012 요점은 이미지를 서버로 보내기 전에 이미지 크기를 줄이는 것입니다.
ferics2 2014 년

pandamatak.com/people/anand/test/crop 의 js 소스를 살펴보십시오. 비슷한 것 같습니다.
luke2012

답변:


161

여기에 내가 한 일이 있으며 훌륭하게 작동했습니다.

먼저 제출되지 않도록 파일 입력을 양식 외부로 이동했습니다.

<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<form id="uploadImageForm" enctype="multipart/form-data">
    <input id="name" value="#{name}" />
    ... a few more inputs ... 
</form>

그런 다음 uploadPhotos크기 조정 만 처리 하도록 함수를 변경했습니다 .

window.uploadPhotos = function(url){
    // Read in file
    var file = event.target.files[0];

    // Ensure it's an image
    if(file.type.match(/image.*/)) {
        console.log('An image has been loaded');

        // Load the image
        var reader = new FileReader();
        reader.onload = function (readerEvent) {
            var image = new Image();
            image.onload = function (imageEvent) {

                // Resize the image
                var canvas = document.createElement('canvas'),
                    max_size = 544,// TODO : pull max size from a site config
                    width = image.width,
                    height = image.height;
                if (width > height) {
                    if (width > max_size) {
                        height *= max_size / width;
                        width = max_size;
                    }
                } else {
                    if (height > max_size) {
                        width *= max_size / height;
                        height = max_size;
                    }
                }
                canvas.width = width;
                canvas.height = height;
                canvas.getContext('2d').drawImage(image, 0, 0, width, height);
                var dataUrl = canvas.toDataURL('image/jpeg');
                var resizedImage = dataURLToBlob(dataUrl);
                $.event.trigger({
                    type: "imageResized",
                    blob: resizedImage,
                    url: dataUrl
                });
            }
            image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
    }
};

보시다시피 canvas.toDataURL('image/jpeg');크기가 조정 된 이미지를 dataUrl adn으로 변경하는 데 사용 하고 있으며 함수 dataURLToBlob(dataUrl);를 호출 하여 dataUrl을 blob으로 변환 한 다음 양식에 추가 할 수 있습니다. Blob이 생성되면 사용자 지정 이벤트를 트리거합니다. 다음은 Blob을 만드는 함수입니다.

/* Utility function to convert a canvas to a BLOB */
var dataURLToBlob = function(dataURL) {
    var BASE64_MARKER = ';base64,';
    if (dataURL.indexOf(BASE64_MARKER) == -1) {
        var parts = dataURL.split(',');
        var contentType = parts[0].split(':')[1];
        var raw = parts[1];

        return new Blob([raw], {type: contentType});
    }

    var parts = dataURL.split(BASE64_MARKER);
    var contentType = parts[0].split(':')[1];
    var raw = window.atob(parts[1]);
    var rawLength = raw.length;

    var uInt8Array = new Uint8Array(rawLength);

    for (var i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], {type: contentType});
}
/* End Utility function to convert a canvas to a BLOB      */

마지막으로 사용자 지정 이벤트에서 blob을 가져 와서 양식을 추가 한 다음 제출하는 이벤트 처리기입니다.

/* Handle image resized events */
$(document).on("imageResized", function (event) {
    var data = new FormData($("form[id*='uploadImageForm']")[0]);
    if (event.blob && event.url) {
        data.append('image_data', event.blob);

        $.ajax({
            url: event.url,
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',
            success: function(data){
               //handle errors...
            }
        });
    }
});

1
멋진 코드, 약간의 오류를 수정하고 수정 한 후 (정확히 무엇을, 정확히 기억하지 못함) 작동하도록했습니다. 그건 그렇고, 당신이 썼을 width *= max_size / width;때 실제로 width *= max_size / height;.
user1111929

2
이 코드는 모바일 장치에서 작동합니까? iOS 및 Android에서?
플레인 워커 2015-09-14

4
@planewalker 저는 실제로 모바일 장치 용으로 특별히 코드를 작성했습니다. 데이터 사용량을 줄입니다.
ferics2 2015-09-15

2
@planewalker, 나는 이것을 작성할 때 iOS에서 테스트하고 있었다. 그것이 당신을 위해 작동하기를 바랍니다.
ferics2 2015 년

3
잘하셨습니다. 공유해 주셔서 감사합니다! (다른 사람들이 언급했지만 식별되지 않은 사소한 오류는 이미지 크기 조정 이벤트 트리거에 있습니다. "url : url"은 "url : dataUrl"이어야합니다.)
Seoras

44

관심이 있다면 typescript 버전을 만들었습니다.

interface IResizeImageOptions {
  maxSize: number;
  file: File;
}
const resizeImage = (settings: IResizeImageOptions) => {
  const file = settings.file;
  const maxSize = settings.maxSize;
  const reader = new FileReader();
  const image = new Image();
  const canvas = document.createElement('canvas');
  const dataURItoBlob = (dataURI: string) => {
    const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
        atob(dataURI.split(',')[1]) :
        unescape(dataURI.split(',')[1]);
    const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
    const max = bytes.length;
    const ia = new Uint8Array(max);
    for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i);
    return new Blob([ia], {type:mime});
  };
  const resize = () => {
    let width = image.width;
    let height = image.height;

    if (width > height) {
        if (width > maxSize) {
            height *= maxSize / width;
            width = maxSize;
        }
    } else {
        if (height > maxSize) {
            width *= maxSize / height;
            height = maxSize;
        }
    }

    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(image, 0, 0, width, height);
    let dataUrl = canvas.toDataURL('image/jpeg');
    return dataURItoBlob(dataUrl);
  };

  return new Promise((ok, no) => {
      if (!file.type.match(/image.*/)) {
        no(new Error("Not an image"));
        return;
      }

      reader.onload = (readerEvent: any) => {
        image.onload = () => ok(resize());
        image.src = readerEvent.target.result;
      };
      reader.readAsDataURL(file);
  })    
};

다음은 자바 스크립트 결과입니다.

var resizeImage = function (settings) {
    var file = settings.file;
    var maxSize = settings.maxSize;
    var reader = new FileReader();
    var image = new Image();
    var canvas = document.createElement('canvas');
    var dataURItoBlob = function (dataURI) {
        var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
            atob(dataURI.split(',')[1]) :
            unescape(dataURI.split(',')[1]);
        var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
        var max = bytes.length;
        var ia = new Uint8Array(max);
        for (var i = 0; i < max; i++)
            ia[i] = bytes.charCodeAt(i);
        return new Blob([ia], { type: mime });
    };
    var resize = function () {
        var width = image.width;
        var height = image.height;
        if (width > height) {
            if (width > maxSize) {
                height *= maxSize / width;
                width = maxSize;
            }
        } else {
            if (height > maxSize) {
                width *= maxSize / height;
                height = maxSize;
            }
        }
        canvas.width = width;
        canvas.height = height;
        canvas.getContext('2d').drawImage(image, 0, 0, width, height);
        var dataUrl = canvas.toDataURL('image/jpeg');
        return dataURItoBlob(dataUrl);
    };
    return new Promise(function (ok, no) {
        if (!file.type.match(/image.*/)) {
            no(new Error("Not an image"));
            return;
        }
        reader.onload = function (readerEvent) {
            image.onload = function () { return ok(resize()); };
            image.src = readerEvent.target.result;
        };
        reader.readAsDataURL(file);
    });
};

사용법은 다음과 같습니다.

resizeImage({
    file: $image.files[0],
    maxSize: 500
}).then(function (resizedImage) {
    console.log("upload resized image")
}).catch(function (err) {
    console.error(err);
});

또는 ( async/ await) :

const config = {
    file: $image.files[0],
    maxSize: 500
};
const resizedImage = await resizeImage(config)
console.log("upload resized image")

정말 좋은 해결책은 결과를 base64로 반환 할 수 있도록 추가 설정을 추가하는 것입니다 (끝에 dataURIToBlob 호출을 무시 함).
Chen

이 오류가 발생합니다.Uncaught (in promise) TypeError: Cannot read property 'type' of undefined
Owen M

1
좋은. unescape를 (<any> window) .unescape로 변경해야했습니다. minSize도 추가했습니다.
로버트 왕

maxSize바이트 또는 kb입니까?
Jagruti

1
귀하의 답변은 이 질문에 답하는 데 도움 되었습니다. 감사합니다.
Syed

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.