如何在Angular上修复照片方向

3
我在我的Angular/Node应用程序中遇到了一个问题,无法正确地定向照片。我的应用程序设置为使用ng2-file-upload将文件从Angular应用程序发送到服务器,Multer和Multer-s3将该照片保存在AWS S3中。然后,我将文件名保存在Postgres数据库中,并使用它通过URL调用照片。
我的问题是,如果照片由iPhone上传(也可能是其他设备,我只尝试过iPhone),则会向左旋转。我已经尝试了几种不同的服务器端选项,但都没有成功。这包括fix-orientation和jpeg-autorotate库。
是否有人在客户端的Angular2+上实现了解决方案?以下是我的服务器端图像上传代码。
aws.config.loadFromPath(path3);
var s3 = new aws.S3();
var fileName = '';
var uploadM = multer({
  storage: multerS3({
    s3: s3,
    bucket: 'XXX',
    acl: 'public-read',
    metadata: function (req, file, cb) {
      console.log(file);
      console.log(req.file);
      cb(null, {fieldName: file.fieldname});
    },
    key: function (req, file, cb) {
      fileName = Date.now().toString() + "-" + (Math.round(Math.random() * 10000000000000000)).toString() + '-' + file.originalname;
      cb(null, fileName)
    }
  })
});

router.post('/upload', uploadM.array('photo', 3), function(req,res) {
    if (res.error) {
      return res.status(400).json({
        message: "Error",
        error: res.error
      });
    }
    return res.status(200).send(fileName);

});


module.exports = router;

以下是我的Angular应用程序代码:

  public uploader:FileUploader = new FileUploader({url: this.devUrl, itemAlias: 'photo'});

  constructor(
    private userS: UserService,
    private authS: MyAuthService,
    private router: Router,
    private itemS: ItemService,
    private uis: UiService) {}

  ngOnInit() {
    this.uploader.onAfterAddingFile = (file)=> { file.withCredentials = false; };

    this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => {
      this.awsUrls.push('AWSURL' + response);
      this.filesUploaded = true;
      this.uploaderLoading = false;
    };

  }

  addItem() {
    this.uploaderLoading = true;
    this.itemS.onNewItem(this.awsUrls)
      .subscribe(data => {
        this.uis.onFlash('Posted Successfully. Add Details!', 'success');
        this.itemS.onSetUpdateItemId(data.id, false);
        this.uploaderLoading = false;
        this.onPhotoAdded();
      }, resp => {
        this.uploaderLoading = false;
        this.uis.onFlash('Error Posting', 'error');
        console.log(resp);
      });
  }

  onUploadClicked() {
    this.uploader.uploadAll();
    this.uploaderLoading = true;
  }

愚蠢的问题,但如果您直接在浏览器中打开图像,它是否处于正确的方向?如果您手动将图像放置在服务器上呢?该图像包含被浏览器忽略的方向数据(我认为是exif)请参见此处这里 - Mathieu de Lorimier
实际上,该图像存储在S3中。因此,当我尝试在浏览器中打开它时,它只会作为JPEG下载。我了解到exif数据被忽略了。我希望有人能告诉我如何在客户端读取该数据,以便我可以使用CSS旋转图像。 - Jonathan Corrin
你有看过这个吗 https://dev59.com/Z2Ij5IYBdhLWcg3wHh3_ - Mathieu de Lorimier
我试过了。看起来是个不错的解决方案,但我无法弄清如何在我的Angular 4应用程序中实现它。 - Jonathan Corrin
4个回答

3
在我看来,理想的解决方案是在将图像发送到S3之前进行旋转,以便仅在一次情况下执行此操作(而不是每次用户查看图像时都要执行)。您可以使用这个库在上传到S3之前确保图像处于正确的方向。请参阅此示例,它可以轻松地集成到您的代码中,无需太多努力。祝好!

我该如何使用ng2-file-upload实现这个?我不确定如何在我的服务器上传之前捕获文件。如果你看我的图像上传代码,那就是我获取与图像请求体暴露的全部内容。我尝试了几个console.logs,但找不到图片在multer之前。 - Jonathan Corrin
可以使用multer中的fileFilter回调函数来实现。此时,文件尚未上传到S3,您可以引用它。请参阅此处的文档。 - Mathieu de Lorimier

3

通过读取Exif数据,在Angular中解决图像方向问题。

安装exif-js

npm i exif-js --save

将EXIF导入到您的组件中

import * as EXIF from 'exif-js';

setImgOrientation 方法有一个名为 file 的参数,该参数是上传的文件对象。inputBase64String 将是该文件的 base64 字符串。
setImgOrientation(file, inputBase64String) {
 return new Promise((resolve, reject) => {
  const that = this;
  EXIF.getData(file, function () {
    if (this && this.exifdata && this.exifdata.Orientation) {
      that.resetOrientation(inputBase64String, this.exifdata.Orientation, function 
     (resetBase64Image) {
        inputBase64String = resetBase64Image;
        resolve(inputBase64String);
      });
    } else {
      resolve(inputBase64String);
    }
    });
   });
  }

  resetOrientation(srcBase64, srcOrientation, callback) {
  const img = new Image();

  img.onload = function () {
  const width = img.width,
    height = img.height,
    canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d');

  // set proper canvas dimensions before transform & export
  if (4 < srcOrientation && srcOrientation < 9) {
    canvas.width = height;
    canvas.height = width;
  } else {
    canvas.width = width;
    canvas.height = height;
  }

  // transform context before drawing image
  switch (srcOrientation) {
    case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
    case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
    case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
    case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
    case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
    case 7: ctx.transform(0, -1, -1, 0, height, width); break;
    case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
    default: break;
  }

  // draw image
  ctx.drawImage(img, 0, 0);

  // export base64
  callback(canvas.toDataURL());
};

  img.src = srcBase64;
}

1
如果所有的iPhone图片都遇到这个问题,你可以在Postgres表中添加一个名为iphone的列,并在上传时将其设置为true,然后使用CSS旋转它。
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
-o-transform: rotate(90deg);
transform: rotate(90deg);

谢谢。我知道这可能是一个解决方案,但我想避免它,但似乎这是唯一的方法!我会给你赏金,并添加我的答案,以便寻找Angular解决方案的人可以参考。 - Jonathan Corrin
1
所以我必须收回我的答案。有一件事我没有考虑到,如果照片是在iPhone上拍摄的,并且在iPhone上查看,图像将会变形,因为IOS出于某种原因读取EXIF数据,即使在Chrome中也是如此。 - Jonathan Corrin
不完整的答案。横向和纵向的 iPhone 图片都以宽/横向形式存储,而纵向图片具有 90° 方向的 exif 数据。因此,您只需将 CSS 应用于纵向图片即可。 - Todd Hale

0

在尝试了几个JS库和基于JS的修复方法后,我需要一种特定于Angular 4的图像方向修复方法。这是我实施的一个解决方法,可能会帮助正在寻找答案的Angular开发人员。

基本上,在上传过程中,我添加了输入,询问用户设备是否为iPhone或iPhone横向照片。使用我的模型中的iPhoneiPhoneLandscape布尔值,如果用户指示使用任一参数拍摄了照片,则将这些变量设置为true。

在客户端,我检索了项目照片,并使用ngStyle将照片旋转90度(如果item.iPhone为true)或180度(如果item.iPhoneLandscape为true)。

// 对于Postgres用户...我的项目模型item.mode.js

module.exports = function(sequelize, DataTypes) {
  var Item = sequelize.define("Item", {
    photos: {
      type: DataTypes.ARRAY(DataTypes.STRING),
      allowNull: false
    },
    name: {
      type: DataTypes.STRING,
      isLowerCase: true,
      allowNull: true,
      defaultValue: null
    },
    description: {
      type: DataTypes.STRING,
      isLowerCase: true,
      allowNull: true,
      defaultValue: null
    },
    condition: {
      type: DataTypes.STRING,
      allowNull: true,
      defaultValue: null
    },
    price: {
      type: DataTypes.INTEGER,
      allowNull: true,
      defaultValue: null
    },
    interval: {
      type: DataTypes.INTEGER,
      allowNull: true,
      defaultValue: 1
    },
    category: {
      type: DataTypes.STRING,
      allowNull: true,
      defaultValue: null
    },
    negotiable: {
      type: DataTypes.BOOLEAN,
      allowNull: true,
      defaultValue: true
    },
    shipping: {
      type: DataTypes.BOOLEAN,
      allowNull: true,
      defaultValue: false
    },
    freeShipping: DataTypes.BOOLEAN,
    length: DataTypes.INTEGER,
    width: DataTypes.INTEGER,
    height: DataTypes.INTEGER,
    weight: DataTypes.INTEGER,
    unavailableDates: {
      type: DataTypes.ARRAY(DataTypes.RANGE(DataTypes.DATE)),
      allowNull: true,
      defaultValue: []
    },
    available: {
      type: DataTypes.BOOLEAN,
      allowNull: true,
      defaultValue: true
    },
    securityDeposit: {
      type: DataTypes.INTEGER,
      defaultValue: 0
    },
    iPhone: {
      type: DataTypes.BOOLEAN,
      defaultValue: false
    },
    iPhoneLandscape: {
      type: DataTypes.BOOLEAN,
      defaultValue: false
    }
  });

// 我在angular的item.model.ts文件中定义了我的模型

export class Item {
  constructor(
    public id?: number,
    public photos?: string[],
    public name?: string,
    public description?: string,
    public condition?: string,
    public price?: number,
    public interval?: number,
    public category?: string,
    public negotiable?: boolean,
    public shipping?: boolean,
    public length?: number,
    public width?: number,
    public height?: number,
    public weight?: number,
    public unavailableDates?: Date[],
    public available?: boolean,
    public iPhone?: boolean,
    public iPhoneLandscape?: boolean,
    public ownerId?: number,
    public owner?: object
  ) {}
}

// 在我的item.service.ts中从服务器调用项目

  onReturnItems() {
    return this.http.get(this.devUrl)
      .map(data => {
        let items: Item[] = data['obj'];
        return items;
      })
      .shareReplay();
  }

// 我的样式函数在item-list.component.ts中调用CSS对象

  styleObject(s: string, item): Object {
     if (s === 'photo') {
      if (item.iPhone) {
        return {'-webkit-transform': 'rotate(90deg)',
          '-moz-transform': 'rotate(90deg)',
          '-ms-transform': 'rotate(90deg)',
          '-o-transform': 'rotate(90deg)',
          'transform': 'rotate(90deg)'}
      } else if (item.iPhoneLandscape) {
        return {'-webkit-transform': 'rotate(180deg)',
          '-moz-transform': 'rotate(180deg)',
          '-ms-transform': 'rotate(180deg)',
          '-o-transform': 'rotate(180deg)',
          'transform': 'rotate(180deg)'}
      }
    }

// 最后,在我的item-list.component.html模板中使用ngStyle和ngFor

<div class="card column is-one-quarter" style="padding: 0; margin-left: 12px; margin-right: 12px;" *ngFor="let item of items">
          <div class="card-image"  (click)="onSetItemId(item.id)">
            <!--<iframe [src]="item.photos[0]"></iframe>-->
            <figure class="image" style="border-radius: 0;">
              <img [src]="item.photos[0]" alt="Image" [ngStyle]="styleObject('photo', item)"  *ngIf="itemsRetrieved" style="border-radius: 0;">
            </figure>
          </div>

希望你能从这个中找到答案!祝你好运!

请注意,此工作仅适用于在 iPhone 上查看照片的情况。 - Jonathan Corrin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接