js,angularjs 图片上传服务器,并解决照片旋转问题,前端图片压缩上传

8 篇文章 0 订阅

手机网站 App 开发时遇到用户拍照上传的场景,照片的大小一般不低于2M ,用户网络不好时会导致上传的时候异常卡顿,因此考虑使用前端图片压缩,再上传服务器.
主要使用canvas来重新绘制前端图片,然后使用toDataURL方法拿到图片被压缩之后的的 Base64 编码数据,上传编码数据,后台解码还原成图片,并根据前端传递的图片旋转信息将图片做相应的旋转处理,废话不多说,直接上代码,虽然说的是 angularjs 的压缩图片上传,其实跟 angularjs 基本没有关系纯粹的 js 代码实现的图片重绘:

前端主要代码

this.uploadPic = function(file, success, fail) {
    if (!/image\/\w+/.test(file.type)) {
        alert("image only please.");
        return false;
    }
    //EXIF为一个 js 读取图片参数工具类,可以用来读取照片被旋转的角度,
    //用作后端代码修正照片旋转,
    EXIF.getData(file,function() {
            //orient即读取到的照片旋转参数
            orient = EXIF.getTag(this,'Orientation');
            var rotate = 0;
            if (orient != null && orient != ""&& orient != 1) {
                switch (orient) {
                case 6:// 需要顺时针(向左)90度旋转
                    rotate = 90;
                    break;
                case 8:// 需要逆时针(向右)90度旋转
                    rotate = 270;
                    break;
                case 3:// 需要180度旋转
                    rotate = 180;
                    break;
                }
            }
            //此处开始读取图片并使用canvas将读取到的信息重新绘制
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function(e) {
                var img = new Image, width = 640, // image
                // resize
                quality = 0.8, // image
                // quality
                canvas = document.createElement("canvas"), 
                drawer = canvas.getContext("2d");
                img.src = this.result;
                img.onload = function() {
                    canvas.width = width;
                    canvas.height = width * (img.height / img.width);
                    //开始绘制图片(压缩之后)
                    drawer.drawImage(img, 0, 0,canvas.width,canvas.height);
                    //获取压缩之后的 base64 编码的数据
                    bodyData = canvas.toDataURL("image/jpeg",quality);
                    //上传图片的 base64 编码信息
                    $http.post("/common/upload-base64?rotate=" 
                               + rotate,bodyData)
                            .success(function(data) {
                                        success(data);
                                    })
                            .error(function(data) {
                                        fail(data);
                                    });
                }
            }
        });
}

在需要上传图片的地方调用uploadPic方法即可,file 传入 input.files[0] success 为上传成功之后的回调方法,fail 为上传失败之后的回调;

后端代码:

@RequestMapping(value = "/upload-base64", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> uploadFileBase64(HttpServletRequest request, 
        HttpServletResponse response,@RequestBody String fileBase64,
        @RequestParam(required = false, value = "rotate", defaultValue = "0") 
        int rotate) throws IOException {
    logger.info("开始上传base64格式的文件:");
    long begin = System.currentTimeMillis();
    //前端上传的数据的开始部分为文件标识信息,在解码之前需要去掉这部分信息
    fileBase64 = fileBase64.replace("data:image/jpeg;base64,", "");
    // Base64解码
    byte[] bytes = Base64.decodeBase64(fileBase64);
    for (int i = 0; i < bytes.length; ++i) {
        if (bytes[i] < 0) {// 调整异常数据
            bytes[i] += 256;
        }
    }
    String separator = "/";
    String realServerPath = request.getSession().getServletContext()
                            .getRealPath(separator);
    Date date = new Date();
    String dateStr = new SimpleDateFormat("yyyyMMdd").format(date);
    String timeStr = new SimpleDateFormat("HHmmssSSS").format(date);
    // 存储的相对路径, 用于返回到页面
    StringBuffer relativePath = new StringBuffer();
    relativePath.append(this.uploadPath).append(separator).append(dateStr);
    StringBuffer uploadDir = new StringBuffer();
    uploadDir.append(realServerPath).append(separator).append(relativePath);
    File dirPath = new File(uploadDir.toString());
    if (!dirPath.exists()) {
        dirPath.mkdirs();
    }
    StringBuffer savedFileName = new StringBuffer();
    savedFileName.append(dateStr).append(timeStr)
                 .append(new Random().nextInt()).append(".").append("jpg");
    String realFilePath = uploadDir + separator + savedFileName;
    File uploadedFile = new File(realFilePath);
    //写入图片
    FileCopyUtils.copy(bytes, uploadedFile);
    logger.info("上传base64文件成功:" + ",耗时:" 
                + (System.currentTimeMillis() - begin) + "ms");
    //校验是否需要对图片做旋转处理
    if (ImageUtil.ANGLE_0 != rotate) {
        ImageUtil.rotatePhonePhoto(realFilePath, rotate);
    }

    Map<String, Object> map = new HashMap<>();
    map.put("relativePath", relativePath + separator + savedFileName);
    return map;
}

旋转图片的代码如下:

/**
     * 将图片旋转一定角度
     *
     * @param fullPath
     * @param angel
     * @return
     */
public static String rotatePhonePhoto(String fullPath, int angel) {
    logger.info("旋转图片:" + fullPath + " 角度:" + angel);
    long begin = System.currentTimeMillis();
    BufferedImage src;
    try {
        ImageIO.setUseCache(false);
        src = ImageIO.read(new File(fullPath));
        int src_width = src.getWidth(null);
        int src_height = src.getHeight(null);
        Rectangle rect_des = calcRotatedSize(new Rectangle(
                             new Dimension(src_width, src_height)), angel);

        BufferedImage res = new BufferedImage(rect_des.width, 
                            rect_des.height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = res.createGraphics();

        g2.translate((rect_des.width - src_width) / 2, 
                     (rect_des.height - src_height) / 2);
        g2.rotate(Math.toRadians(angel), src_width / 2,
                   src_height / 2);

        g2.drawImage(src, null, null);
        //注意此处使用 jpg 格式,如果使用其他格式编码会出现效率的诸多问题
        ImageIO.write(res, "jpg", new File(fullPath));

    } catch (IOException e) {
        logger.error("", e);
    }
    logger.info("旋转图片:" + fullPath + " 角度:" + angel 
                + "  耗时:" + (System.currentTimeMillis() - begin) + "ms");
    return fullPath;

}

    /**
     * 计算新照片的大小
     *
     * @param src
     * @param angel
     * @return
     */
public static Rectangle calcRotatedSize(Rectangle src, int angel) {
    // if angel is greater than 90 degree, we need to do some conversion
    if (angel >= 90) {
        if (((angel / 90) % 2) == 1) {
            int temp = src.height;
            src.height = src.width;
            src.width = temp;
        }
        angel = angel % 90;
    }

    double r = Math.sqrt((src.height * src.height) 
               + (src.width * src.width)) / 2;
    double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
    double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
    double angel_dalta_width = Math.atan((double) src.height / src.width);
    double angel_dalta_height = Math.atan((double) src.width / src.height);

    int len_dalta_width = (int) (len * Math.cos(Math.PI 
                                 - angel_alpha - angel_dalta_width));
    int len_dalta_height = (int) (len * Math.cos(Math.PI 
                                  - angel_alpha - angel_dalta_height));
    int des_width = src.width + (len_dalta_width * 2);
    int des_height = src.height + (len_dalta_height * 2);
    return new Rectangle(new Dimension(des_width, des_height));
}

以上代码即可解决图片压缩上传,照片翻转的问题
如果想上传图片原图,并在后端做图片的选择操作,最好在后端直接读取图片,EXIF.js读取图片信息时非常慢,但由于图片重新绘制会丢失照片原有的旋转信息因此前端压缩时必须读取照片旋转角度以便后台修正,如果前端直接上传原图,可以在后台使用

 <dependency>
            <groupId>com.drewnoakes</groupId>
            <artifactId>metadata-extractor</artifactId>
            <version>2.10.1</version>
  </dependency>

来读取照片信息,并获取需要旋转的角度
主要代码如下:

/**
     * 获取图片的旋转角度
     *
     * @param filePath
     * @return 图片旋转角度
     */
public static int getRotateAngleForPhoto(String filePath) {
        File file = new File(filePath);
        int angle = ANGLE_0;
        Metadata metadata;
        try {
            metadata = JpegMetadataReader.readMetadata(file);
            Directory directory = metadata
                      .getFirstDirectoryOfType(ExifIFD0Directory.class);
            if ((null != directory) && directory.containsTag(
                                  ExifIFD0Directory.TAG_ORIENTATION)) {
                // Exif信息中方向
                int orientation = directory.getInt(
                                  ExifIFD0Directory.TAG_ORIENTATION);
                // 原图片的方向信息
                if (6 == orientation) {
                    // 6旋转90
                    angle = ANGLE_90;
                } else if (3 == orientation) {
                    // 3旋转180
                    angle = ANGLE_180;
                } else if (8 == orientation) {
                    // 8旋转90
                    angle = ANGLE_270;
                }
            }
        } catch (JpegProcessingException e) {
            logger.error("", e);
        } catch (MetadataException e) {
            logger.error("", e);
        } catch (IOException e) {
            logger.error("", e);
        }
        return angle;
    }

不压缩直接上传的前端代码如下,参数的意义与之前压缩上传的一致

this.uploadPicNotCompress = function(file, success, fail) {
    if (!/image\/\w+/.test(file.type)) {
        alert("image only please.");
        return false;
    }
    var formdata = new FormData(); // 初始化一个FormData实例
    formdata.append('file', file); // file就是图片或者其他你要上传的formdata
            $http.post("common/upload", formdata, {
                                        transformRequest : angular.identity,
                                        headers : {
                                            'Content-Type' : undefined
                                        }})
                    .success(function(data) {
                                success(data);
                            })
                    .error(function(data) {
                                fail(data);
                            });
                    }

exif.js下载链接:http://pan.baidu.com/s/1jItDFMI 密码:m98m

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过以下步骤使用 HTML 和 JavaScript 实现图片压缩上传服务器: 1. 在 HTML 文件中创建一个 input 元素,使用户可以选择要上传的图片文件: ```html <input type="file" id="file-input"> ``` 2. 使用 JavaScript 获取用户选择的图片文件,并将其转换为 base64 格式: ```javascript const fileInput = document.getElementById('file-input'); const file = fileInput.files[0]; const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function() { const base64Image = reader.result; }; ``` 3. 使用 JavaScript 创建一个 Image 对象,加载用户选择的图片文件,并在加载完成后将其绘制到一个 Canvas 上: ```javascript const img = new Image(); img.src = base64Image; img.onload = function() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置 Canvas 大小为图片大小 canvas.width = img.width; canvas.height = img.height; // 在 Canvas 上绘制图片 ctx.drawImage(img, 0, 0); // 获取 Canvas 上的压缩后的图片数据 const compressedImage = canvas.toDataURL('image/jpeg', 0.5); }; ``` 在这个例子中,我们将图片压缩为质量为 50% 的 JPEG 格式。 4. 创建一个 XMLHttpRequest 对象,将压缩后的图片数据作为请求体发送到服务器: ```javascript const xhr = new XMLHttpRequest(); xhr.open('POST', '/upload'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(`image=${encodeURIComponent(compressedImage)}`); ``` 在这个例子中,我们将压缩后的图片数据作为 image 参数发送到服务器。 完整的 HTML 和 JavaScript 代码如下: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>图片上传</title> </head> <body> <input type="file" id="file-input"> <button id="upload-btn">上传</button> <script> const fileInput = document.getElementById('file-input'); const uploadBtn = document.getElementById('upload-btn'); fileInput.addEventListener('change', function() { const file = fileInput.files[0]; const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function() { const base64Image = reader.result; const img = new Image(); img.src = base64Image; img.onload = function() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置 Canvas 大小为图片大小 canvas.width = img.width; canvas.height = img.height; // 在 Canvas 上绘制图片 ctx.drawImage(img, 0, 0); // 获取 Canvas 上的压缩后的图片数据 const compressedImage = canvas.toDataURL('image/jpeg', 0.5); uploadBtn.addEventListener('click', function() { const xhr = new XMLHttpRequest(); xhr.open('POST', '/upload'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(`image=${encodeURIComponent(compressedImage)}`); }); }; }; }); </script> </body> </html> ``` 请注意,这只是一个简单的示例,实际应用中还需要考虑更多的因素,例如图片大小限制、上传进度显示、错误处理等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值