【问题标题】:React/Node Trouble sending ImageData to serverReact/Node 无法将 ImageData 发送到服务器
【发布时间】:2019-04-10 20:09:53
【问题描述】:

我遇到了一个问题,我在将画布的 imageData 发送到我的服务器进行处理时遇到了一些问题。

这是我发送数据的 react/redux 操作:

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

以下是特征方法的示例:

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。 正如 Kaiido 所说,我可能想对那个数组进行切片,我试过了,但没有用。

我应该如何切片? 180000个第一个? 180000持续一个?其他方式?

【问题讨论】:

  • 您是否使用 Postman 测试过您的 API?路由是否正常工作并接收正确的数据?
  • 不,不是用邮递员而是直接用前台,问题是接收数据而不是我发送的确切数据(正如我在帖子中所说)

标签: node.js reactjs canvas redux imagedata


【解决方案1】:

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

这是 JSON.stringify 的反面,当您通过 stringify 发送数据但在路由中不将其解析回原始对象时需要。

【讨论】:

  • 感谢您的回复,但我想我已经在这样做了,我的路线上方有 bodyParser 中间件:app.use(bodyParser.json({ limit: '50mb' })) 做同样的事情吗?编辑:测试它得到错误Unexpected token o in JSON at position 1 这意味着它已经被解析了..
  • 对,您应该检查req.body.imgData,因为发送时您正在传递Json.Stringify({ imgData : payload }) instead of simple Json.Stringify(payload)。 ImgData 是 body 中包含有效负载的对象。
  • 也最好取消注释标题。 Console.log() 发送前没问题。您在路线上检查过Console.log 吗?您似乎在传输过程中丢失了数据。
  • 是的,我正在做测试,这就是它被评论的原因。是的,我知道,问题是我丢失了数据(我的高度/宽度键),我的数组(imageData.data)变成了一个键/对对象,它们都失去了它们的类型成为对象
【解决方案2】:

JSON 只处理很少的类型:字符串、数字、数组、对象、布尔值和 null。

没有可枚举属性的 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 属性没有以正确的方式进行字符串化。

确实,当 JSON.stringified 被转换为简单对象时的 TypedArray,其所有索引都设置为键。 原本应该只占用 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
*/

现在在您的服务器上,为了检索您请求的所有部分,您将拥有to grab the form-data content,并以文件上传的形式访问图像数据。

然后您必须读取这个上传的文件(例如,使用 fs.readFile(temp_path) 并直接从返回的 Buffer 中执行操作(自 Buffers in node are Uint8Array 起)。

一旦你的操作完成,你可以直接发送这个修改后的缓冲区作为响应。

现在,您只需要请求将 Response 作为 ArrayBuffer 使用,并从该 ArrayBuffer 生成新的 ImageData:

//... 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() 您收到的 Array in为了获取元数据和实际的图像数据。

【讨论】:

  • 感谢您抽出宝贵时间向我解释,在您的帮助下,我已接近成功。只是我在编辑中解释的一个问题。如果你有更多的时间陪我,看看它
猜你喜欢
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-30
  • 2014-03-22
  • 1970-01-01
  • 2021-05-01
相关资源
最近更新 更多