React / Node Неполадка при отправке ImageData на сервер - PullRequest
0 голосов
/ 10 апреля 2019

У меня возникла проблема, когда у меня возникли некоторые проблемы с отправкой изображения imageData canvas на мой сервер для его обработки.

Вот мое действие реагирования / сокращения для отправки данных:

export const edit = (e) => dispatch => {
  let payload = store.getState().image.imageData
  payload = payload[payload.length-1]
  console.log(payload)
  const id = e.target.id ? e.target.id : e.target.parentElement.id
  fetch(`/api/editing/${id}`, {
    method: 'POST',
    // headers: {
    //   "Content-Type": "application/json"
    // },
    body: JSON.stringify({ imgData: payload })
  })
  .then(res => res.json())
  .then(json => {
    dispatch({ type: UPDATE_IMAGE, payload: json.imgData })
    // handle response display
  })
}

Вот моекак я обрабатываю запрос:

const traitement = require('../../processing/traitement')

router.post('/:type', (req, res) => {
  const { imgData } = req.body
  const method = req.params.type
  const imgDataProcessed = traitement[method](imgData)
  return res.status(200).json({ imgData: imgDataProcessed })
})

Вот пример того, как выглядит метод traitement:

negatif: function(imgData) {
  console.log(imgData)
  var n = imgData.data.length;
  for(i=0; i<n; i+=4){
    imgData.data[i] = 255 - imgData.data[i];
    imgData.data[i+1] = 255 - imgData.data[i+1];
    imgData.data[i+2] = 255 - imgData.data[i+2];
    imgData.data[i+3] = 255;        
  }
  return imgData;
},

console.log () непосредственно перед отправкой (что я ожидаю отправить)):

ImageData {data: Uint8ClampedArray(180000), width: 300, height: 150}
data: Uint8ClampedArray(180000) [0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 
  255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 
  255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 
  0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 
  255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 
  0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 
  255, …]
height: 150
width: 300
__proto__: ImageData

Я не могу вставить то, что получаю с сервера, но теряю ключи ширины, высоты, typeof - это Object вместо ImageData, а ключ данных - это typeof Object вместо Uint8ClampedArray.

Итак, мой вопрос: как я могу сделать так, чтобы мой маршрут получил доступ к тем же данным, которые я отправляю, чтобы я мог их обработать?

Как вы можете видеть, я отправляю их в виде строк json иУ меня на сервере есть промежуточное программное обеспечение json bodyparser, может, оно оттуда.Я также думаю о заголовке типа содержимого

РЕДАКТИРОВАТЬ: Благодаря Kaiido я изменил свой код таким образом, который, кажется, работает на 1 исключение

Как я изменил свой код, перед:

  let payload = store.getState().image.imageData
  payload = payload[payload.length-1]
  const id = e.target.id ? e.target.id : e.target.parentElement.id;
  console.log(payload)
  const meta = {
    width: payload.width,
    height: payload.height
  }
  const formData = new FormData();
  formData.append("meta", JSON.stringify(meta));
  formData.append("data", new Blob([payload.data.buffer]))
  fetch(`/api/editing/${id}`, {
    method: "POST",
    body: formData
  })
  .then(res => res.arrayBuffer())
  .then(buffer => {
    console.log(buffer)
    const data = new Uint8ClampedArray(buffer)
    console.log(data.length)
    const newImg = new ImageData(data, payload.width, payload.height)
    return newImg
  })

назад:

router.post('/:type', (req, res) => {
  let form = new formidable.IncomingForm()
  form.parse(req, (err, fields, files) => {
    fs.readFile(files.data.path, (err, data) => {
      const imgProcessed = traitement[req.params.type](data)
      console.log(imgProcessed.length)
      return res.status(200).json([imgProcessed])
    })
  })
})

Осталось 1 проблема: при условии, что для тестирования я использую изображение размером 150 * 300 пикселей, мой массив данных должен иметь длину 180000 (300 * 150 * 4)который, пока я не отправлю ответ сервера.Когда фронт получает ответ, он вызывает res.arrayBuffer(), затем он создает новый Uint8ClampedArray, но тогда моя длина не больше 180000, но в этом случае 543810.Как сказал Кайидо, я мог бы захотеть нарезать этот массив, который я пробовал, но не работает.

Как мне его нарезать?180000 первых?180000 длится один?как-нибудь иначе?

Ответы [ 2 ]

1 голос
/ 11 апреля 2019

JSON имеет только несколько типов, которые он обрабатывает: строки, числа, массивы, объекты, логические значения и ноль.

ImageData, который не имеет перечисляемых свойств, получит JSON.stringified до "{}", пустой объект.

const img = new ImageData(6, 6);
img.data.fill(0xFF);
console.log('JSON', JSON.stringify(img)); // "{}"

const props = Object.getOwnPropertyDescriptors(img);
console.log('own properties', props); // {}

Вы потеряете всю информацию.Поэтому, чтобы обойти это, вам нужно будет отправить все это вручную в вашем запросе.
Звучит довольно просто, просто переместив необходимые значения в другой объект:

const img = new ImageData(6,6);
const dataholder = (({width, height, data}) => ({width, height, data}))(img);

console.log(JSON.stringify(dataholder));

Однако, вы заметите, что свойство data неправильно структурировано.

Действительно, TypedArray, когда JSON.stringified преобразуется в простой объект со всеми его индексами, установленными в качестве ключей.И то, что должно было занять всего 144 байта для представления данных изображения размером 6px * 6px, теперь занимает 1331 байт.

const typedArray = new Uint8ClampedArray(6*6*4).fill(0xFF);

console.log(JSON.stringify(typedArray));

const as_string = new Blob([JSON.stringify(typedArray)]);
const as_arraybuffer = new Blob([typedArray]);

console.log('as string', as_string.size, 'bytes');
console.log('as arraybuffer', as_arraybuffer.size, 'bytes');

Я позволю вам сделать математику для больших изображений, но вы могли бы рассмотреть другой подход ...


Вместо отправкистроковая версия этого Uint8ClampedArray, вам лучше отправить базовый ArrayBuffer. Может показаться, что вы пишете больше кода, но вы сэкономите пропускную способность и, возможно, даже деревья.

Таким образом, чтобы отправить ваши ImageData на ваш сервер, вам нужно будет

const img = new ImageData(6, 6);
img.data.fill(0xFF);

const meta = {
  width: img.width,
  height: img.height
  // add more metadata here if needed
};
const formdata = new FormData();
formdata.append("meta", JSON.stringify( meta ))
formdata.append("data", new Blob([img.data.buffer]));

console.log(...formdata); // [key, value], [key, value]
// here data is a File object and weights only 144 bytes

/* // send the formdata to your server
fetch('yoururl', {
  method: 'POST',
  body: formdata
})
.then(response => response.arrayBuffer())
... // we'll come to it later
*/

Теперь на вашем сервере, чтобы получить все части вашего запроса, у вас будет для захвата данных формы , иполучить доступ к данным изображения при загрузке файла.

Затем вам нужно будет прочитать этот загруженный файл (например, используя fs.readFile(temp_path) и напрямую выполнить манипуляции из возвращенного буфера (так как Буферы в узле - это Uint8Array).).

Как только ваши манипы будут готовы, вы можете напрямую отправить этот модифицированный буфер в качестве ответа.

Теперь вам нужно будет просто попросить ответиспользуется как ArrayBuffer и для создания новых ImageData из этого ArrayBuffer:

//... see above
fetch('yoururl', {
  method: 'POST',
  body: formdata
})
// request as ArrayBuffer
.then(response => response.arrayBuffer())
.then(buffer => {
  // create a new View over our ArrayBuffer
  const data = new Uint8ClampedArray(buffer);
  const new_img = new ImageData(data, img.width, img.height);
  return new_img;
});

Здесь предполагается, что ваш фронт не нуждается в метаданных из ответа сервера (потому что он у вас уже есть).
Если вам нужны метаданные, то вы можете добавить их в начале ответа ArrayBuffer (назначьте слот, чтобы вы знали его размер, затем slice () ваш полученный массив в порядкесхватить обаметаданные и фактические данные изображения.

1 голос
/ 10 апреля 2019

Do

const { imgData } = JSON.parse(req.body)

это обратное JSON.stringify, и оно необходимо, когда вы отправляете данные через stringify, но при маршруте не анализируете их обратно в исходный объект.

...