Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] http fetch fails to send form with correct image encoding #4312

Closed
evbo opened this issue Jun 9, 2022 · 8 comments
Closed

[bug] http fetch fails to send form with correct image encoding #4312

evbo opened this issue Jun 9, 2022 · 8 comments

Comments

@evbo
Copy link

evbo commented Jun 9, 2022

Describe the bug

If you call the http form function to produce a Body that includes both a Byte array file the correct mime type isn't used and what gets uploaded is a unicode text file instead of an image.

Example:

Reproduction

Call the fetch api with a body created by Body.form(...) with an end result similar to:

{someKey: {file: byteArray, mime: 'image/png', fileName: 'aFile.png'}

Where "someKey" is what your server expects to be the key in the form containing the file and byteArray is an array of numbers, each number between (-128, 127).

An example byte array (in plain text) is attached (for the Tauri logo):
128x1282x

The file will be correctly uploaded, however it will simply be a text file, not binary (image) if in linux you run the command file -i img.png it will show a mime type of plain text.

As a control simply perform the upload specifying an absolute path to the image file:

{someKey: {file: 'path/to/file', mime: 'image/png', fileName: 'filename.png'}}

And this works great! So we know it is only an issue when supplying a byte array as file

Expected behavior

The file should have mime type of image/png

Platform and versions

Linux (Ubuntu 20.04, Centos 7, Windows 10)

[build-dependencies]
tauri-build = { version = "1.0.0-rc.12", features = [] }

[dependencies]
tauri = { version = "1.0.0-rc.14", features = ["fs-read-file", "fs-write-file", "http-all", "http-multipart", "shell-open"] }

Stack trace

No response

Additional context

I tried adding reqwest-client but that resulted in it completely not uploading at all with my server rejecting:

org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found

@evbo evbo added the type: bug label Jun 9, 2022
@evbo
Copy link
Author

evbo commented Jun 10, 2022

Note: an example server to test this aganst is Jira. Run, setup a project, create an issue:
https://hub.docker.com/r/atlassian/jira-software

Then post an attachment to an issue:
https://docs.atlassian.com/software/jira/docs/api/REST/8.22.3/#issue/{issueIdOrKey}/attachments-addAttachment

@lucasfernog
Copy link
Member

Can you try changing the content-type header to multipart/form-data and enabling the http-multipart Cargo feature?

@evbo
Copy link
Author

evbo commented Jun 10, 2022

@lucasfernog sorry I'm not understanding, don't I already show the http-multipart feature flag above? Or is there another step necessary to enable besides adding it to cargo.toml?

Also, I add the following headers to the request (in addition to auth):

"content-type" -> "multipart/form-data", "X-Atlassian-Token" -> "no-check", "accept" -> "application/json", "user-agent" -> "custom-agent"

Keep in mind I show that this works with specifying a path for file, it;s just specifying a byte array when it fails to encode properly. I would use the path if I could, but browser file pickers intentionally do not share file path for security reasons to javascript.

@lucasfernog
Copy link
Member

ahh you have the feature, sorry didn't see that. I'll investigate this issue, thanks for the report.

@lucasfernog
Copy link
Member

@evbo I just tried it and it work both with a file path and a byte array. Here's my code:

const file = await readBinaryFile('/path/to/image.png')
		
result = await fetch('http://localhost:8080/rest/api/2/issue/TEST-25/attachments', {
  method: 'POST',
  body: Body.form({
    'file': {
      file, mime: 'image/png', fileName: 'image.png'
    }
  }),
  headers: { 'Content-Type': 'multipart/form-data', Authorization: 'Basic bHVjYXNmZXJub2c6MDQwODk2NTA0MjI=', 'X-Atlassian-Token': 'no-check' }
})

Note that if you use each number between (-128, 127) as you said it'll end up being a weird attachment in Jira anyway since that's not a valid image byte array.

Also, the multipart logic is the same when using a file path or a byte array (internally we just read the file and get its bytes to send), so both ways should work. If you want me to try something else, please share some code with us.

@lucasfernog
Copy link
Member

Actually there's an issue when using file as a path, i'll push a change to fix it.

@lucasfernog
Copy link
Member

Also found the issue for the reqwest usage, thanks for catching that :)

@evbo
Copy link
Author

evbo commented Jun 11, 2022

Thanks @lucasfernog and @FabianLars I'm glad I provided some assistance in the end!

Now I wish I read this sooner! Here's the proper way to encode files as byte arrays using FileReader in JS:
https://stackoverflow.com/a/31435538/1080804

There's a lot of examples online instructing to read files as URLs or Text - that's only useful for HTML tags like <img> to reference as their src, etc where they need plaintext representations. Never try to re-encode binary files starting from text (lesson learned!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants