От автора: в настоящее время я работаю над небольшим побочным проектом, где я загружаю изображения на сервер (используя поле загрузки файлов и дополнительно Web Share Target), чтобы сделать их доступными для других. Хотя первая рабочая версия была готова довольно быстро, существовала одна проблема — при загрузке портретного изображения с телефона изображение отображалось с неправильной ориентацией.
В этой статье вы узнаете о текущем состоянии дел с ориентацией изображений в Веб, о том, как исправить ориентацию изображений с помощью Node.js, и о том, как браузеры будут справляться с этим в будущем.
Текущее состояние
Если вы просматриваете это изображение (оно взято из примеров ориентации с помощью exif), оно может корректно отображаться в портретном режиме или неправильно отображаться с поворотом влево на 90 градусов.
В браузере на iOS вы, скорее всего, видите слово top на самом деле вверху, а в другом браузере, вы можете видеть слово top слева. Причина этого заключается в том, что данные EXIF для этого изображения имеют информацию для ориентации Rotate 90 CW.
Когда вы встраиваете изображение с помощью img или background-image, изображение будет отображаться неправильно во всех браузерах, кроме браузеров на iOS. Однако если вы откроете изображение напрямую, оно будет правильно отображаться в Firefox, Chrome и Safari, но все равно будет отображаться повернутым в Edge и Internet Explorer. Какая последовательность!
Свойство ориентации изображения
Когда я искал возможные способы исправить ориентацию с помощью CSS или JavaScript, я нашел информацию о свойстве image-orientation.
На данный момент это свойство поддерживается только в Firefox, и вы можете использовать его таким образом, чтобы браузеры считывали данные EXIF и исправляли ориентацию:
img { image-orientation: from-image; }
При поиске дополнительной информации я также обнаружил, что это свойство уже устарело, что поначалу меня смущало, потому что оно было бы для меня простым способом исправить проблему с ориентацией изображения, если бы поддерживалось всеми браузерами.
После прочтения еще одной статьи на Github я узнал, что план заключается в том, что все браузеры будут поддерживать ориентацию EXIF в будущем и, следовательно, будут использовать image-orientation: from-image по умолчанию. Отлично, вот что мне нужно!
Исправление ориентации с помощью Node.js
До сих пор мы узнали, что у браузеров довольно много разногласий по поводу того, как работать с ориентацией EXIF, и не существует кросс-браузерного способа легко исправить это. Короче говоря, это все довольно беспорядочно, и, поскольку мы не хотим, чтобы некоторые пользователи видели неправильные изображения, давайте посмотрим, как мы можем это исправить, используя Node.js.
Для начала нам нужна форма для загрузки изображений:
<form class="form" action="./upload" enctype="multipart/form-data" method="POST"> <label for="file">Select image: </label> <input type="file" required name="file" multiple="" accept="image/*"> <input type="submit" value="Upload your photos"> </form>
После того, как вы выберете изображение и отправите форму, будет отправлен POST-запрос к /upload, поэтому давайте рассмотрим маршрут загрузки:
app.post('/upload', upload.array('file'), async(req, res, next) => { const images = req.files; for (image of images) { await correctOrientation(image); } res.redirect('./show'); });
Мы используем multer в качестве промежуточного программного обеспечения для обработки изображений и для каждого изображения, которое мы вызываем, correctOrientation для корректировки ориентации.
Прежде, чем мы рассмотрим correctOrientation, вот конфигурация multer, который мы используем для сохранения изображений в папке /uploads.
var storage = multer.diskStorage({ destination: function(req, file, cb) { cb(null, path.join(__dirname, "uploads")); }, filename: function(req, file, cb) { cb(null, Date.now() + '__' + file.originalname); } }) const upload = multer({ storage: storage });
Теперь давайте рассмотрим на интересную часть — коррекцию ориентации изображения:
const readFileAsync = async(file) => { return await new Promise((resolve, reject) => { fs.readFile(file, async(err, data) => { err ? reject(err) : resolve(data); }); }); }; const correctOrientation = async(image) => { let imageOrientation = false; let rotateDeg = 0; const buffer = modifyExif(await readFileAsync(path.join(__dirname, "uploads") + '/' + image.filename), data => { imageOrientation = data && data["0th"] && data["0th"]["274"] ? data["0th"]["274"] : false; if (imageOrientation) { if (imageOrientation === 1) { imageOrientation = false; } else { data["0th"]["274"] = 1; // reset EXIF orientation value } } }); if (imageOrientation) { switch (imageOrientation) { case 3: rotateDeg = 180; break; case 6: rotateDeg = 270; break; case 8: rotateDeg = 90; reak; default: rotateDeg = 0; break; } Jimp.read(buffer, (err, lenna) => { if (err) { console.log('err', err); return; } lenna .rotate(rotateDeg) // correct orientation .write(path.join(__dirname, "uploads") + '/' + image.filename); // save }); } };
Сначала мы определяем readFileAsync, что превращает функцию fs.readFile в промис. Теперь давайте посмотрим, что у нас содержится в самой функции. Во-первых, мы используем пакет modify-exif для установки значения ориентации EXIF для каждого изображения равным 1 (значение по умолчанию), если оно еще не было 1.
После того, как мы изменили информацию EXIF, мы теперь используем jimp, чтобы повернуть изображение в соответствии со значением ориентации EXIF, которое мы получили ранее, и повторно сохранить изображение. И все — теперь все картинки корректно отображаются в каждом браузере. Вы можете найти полный пример на Github.
Заключение
На данный момент единственным браузером, поддерживающим ориентацию EXIF, являются браузеры на iOS, но есть надежда, что другие браузеры рано или поздно также примут это. Таким образом, эта проблема будет решена автоматически, и у нас, разработчиков, будет одной проблемой меньше.
Однако на данный момент единственный способ решить проблему с ориентацией — это удалить данные об ориентации EXIF и соответственно повернуть изображение, что можно сделать вручную или с помощью любимой серверной части. Также было бы возможно исправить это на стороне клиента, используя JavaScript, но я не советую это, так как мы должны как можно меньше тяжелой работы отдавать на аутсорсинг клиенту и, следовательно, пользователю.
Удачного кодирования и не забудьте сообщить вашему любимому браузеру, что они должны принять ориентацию EXIF и решить эту проблему.
Автор: Michael Scharnagl
Источник: https://justmarkup.com
Редакция: Команда webformyself.