sourcetip

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

fileupload 2023. 11. 4. 13:15
반응형

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

저는 스택 오버플로우에 대한 몇 가지 다른 게시물과 이 질문에 답하는 질문을 발견했습니다.저는 기본적으로 이 게시물과 같은 것을 실행하고 있습니다.

그래서 여기 제 문제가 있습니다.사진을 올릴 때 나머지 양식도 함께 제출해야 합니다.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...
            }
        }
    });
};

파일 입력을 양식 밖으로 옮기고 크기를 조정한 이미지의 값으로 설정한 양식에 숨겨진 입력을 갖는 것에 대해 생각해 보았습니다.그런데 이미 양식에 있는 이미지만 교체해도 되는지 궁금합니다.

여기 제가 한 일이 있는데 정말 잘 되었습니다.

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

<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');크기가 조정된 이미지를 데이터로 변경하려면Url을 누른 다음 함수를 호출합니다.dataURLToBlob(dataUrl);데이터 Url을 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      */

마지막으로, 커스텀 이벤트에서 블랍을 가져와 양식을 추가한 후 제출하는 제 이벤트 핸들러입니다.

/* 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...
            }
        });
    }
});

관심있는 사람이 있다면 타이프스크립트 버전을 만들었습니다.

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")

저와 같은 몇몇 분들이 오리엔테이션 문제를 겪는다면 저는 여기에 해결책을 조합했습니다.

https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0

2022년에는 우리가 사용할 수 있는 새로운 API가 몇 가지 있습니다.이것이 제가 생각해낸 해결책입니다.우리는 파일 리더 API나 이미지 온로드 콜백을 건드릴 필요가 없습니다.

다음 코드는 파일 객체 또는 Blob 객체를 받아들여서 잘라낸, 중심을 맞춘, 리사이징된 이미지의 Blob을 출력하고 이를 webp로 변환합니다.


export default async (file, size) => {
  size ??= 256

  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')

  canvas.width = size
  canvas.height = size

  const bitmap = await createImageBitmap(file)
  const { width, height } = bitmap

  const ratio = Math.max(size / width, size / height)

  const x = (size - (width * ratio)) / 2
  const y = (size - (height * ratio)) / 2

  ctx.drawImage(bitmap, 0, 0, width, height, x, y, width * ratio, height * ratio)

  return new Promise(resolve => {
    canvas.toBlob(blob => {
      resolve(blob)
    }, 'image/webp', 1)
  })
}

저는 파일 리더기를 실제로 사용하지 않고 저만의 버전을 만들었습니다.대신 사용합니다.createObjectUrl대부분의 현대 브라우저가 이를 지원합니다.

/**
 * Function scaling an image from a file input to specified dimensions
 * If the specified dimensions are not proportional to image dimensions the output image will be cropped at center
 *
 * @param file {File} Input file of a form
 * @param dimensions {{width: number, height: number}} Dimenstions of the output image
 * @returns {Promise<Blob | null>} Promise resolving to a scale image or a null if provided an invalid file type
 */
export async function scaleImageBeforeUpload(file: File, dimensions: {width: number, height: number}): Promise<Blob | null> {
    // ensure the file is an image
    if (!file.type.match(/image.*/)) return null;

    const image = new Image();
    image.src = URL.createObjectURL(file);

    await new Promise<Event>((res) => image.onload = res);
    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d", {alpha: true});

    canvas.width = dimensions.width;
    canvas.height = dimensions.height;

    if (image.height <= image.width) {
        const scaleProportions = canvas.height / image.height;
        const scaledWidth = scaleProportions * image.width;
        context.drawImage(image, (canvas.width - scaledWidth)/2, 0, scaledWidth, canvas.height);
    }
    else {
        const scaleProportions = canvas.width / image.width;
        const scaledHeight = scaleProportions * image.height;
        context.drawImage(image, 0, (canvas.height - scaledHeight)/2, canvas.width, scaledHeight);
    }

    return new Promise((res) => canvas.toBlob(res));
}

이미지 크기를 최대 파일 크기에 맞게 조정하는 가장 비효율적인 방법

    function processImage(base64) {
        return new Promise(function(resolve, reject) {
            var img = new Image();
            img.onload = function() {
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
                var width = img.width;
                var height = img.height;
                var resizedBase64 = null;
                while (resizedBase64 == null) {
                    console.log("width: " + width + " height: " + height);
                    canvas.width = width;
                    canvas.height = height;
                    ctx.drawImage(img, 0, 0, width, height);
                    if (canvas.toDataURL('image/png').length > maxFileSize) {
                        width = width * 0.9;
                        height = height * 0.9;
                    } else {
                        resizedBase64 = canvas.toDataURL('image/png')
                    }
                }
                console.log("width: " + width + " height: " + height);
                resolve(resizedBase64);
            };
            img.src = base64;
        });
    }

언급URL : https://stackoverflow.com/questions/23945494/use-html5-to-resize-an-image-before-upload

반응형