Image uploaded to IPFS is just a really long string of characters. ".jpg" filetype/extension gets lost

Hi all, I am trying to figure out why my webform can’t upload a jpg to IPFS. Quick details: react-dropzone for frontend webform & nft.storage (this an NFTHack project) for IPFS pinning.

When I upload an image via nft.storage/new-file/ everything works as expected (~5MB): ipfs://bafybeidndzo5rwfe4a7ryhbnriiffp5nwy72cz63un6xytuifrlz3lm3t4/

When using my broken code, it seems that the hash of the image is the only thing written to IPFS (~12MB):
ipfs://bafybeiffhp3sfb4qa4o5t63po6prxvvskrkpxlnx32muvqdthtlekby6fe/
(these both used the same .jpg from my computer, so I have absolutely no idea why the text-only upload is twice the size?)

I’m pretty new to programming in general so I’m just going to copy/paste the parts that I think might be causing this trouble.

web3.js
const getFileBuffer = async (file) => {
return new Promise((res, rej) => {
  let reader = new FileReader();

  reader.addEventListener("loadend", (e) => res(e.target.result));
  reader.addEventListener("error", rej);

  reader.readAsArrayBuffer(file);
});

};
//…
const mintMedia = async (media, name, description, royalty, royalties) => {

console.log(royalties);

const metadataJSON = generateMetadata("zora-20210101", {

  description: description,

  mimeType: media.type,

  name: name,

  version: "zora-20210101",

});

const mediaBuffer = await getFileBuffer(media);

const contentHash = sha256FromBuffer(Buffer.from(mediaBuffer));

const metadataHash = sha256FromBuffer(Buffer.from(metadataJSON));

let formData = new FormData();

formData.append("media", media);

formData.append("name", name);

formData.append("metadata", metadataJSON);

const upload = await axios.post("/api/upload", formData, {

  headers: {

    "Content-Type": "multipart/form-data",

  },

});



const { mediaCID, metadataCID } = upload.data;

const mediaUrl = `https://${mediaCID}.ipfs.dweb.link`;

const metadataUrl = `https://${metadataCID}.ipfs.dweb.link`;

const mediaData = constructMediaData(

  mediaUrl,

  metadataUrl,

  contentHash,

  metadataHash

);
/pages/mint.js

const Mint = () => {

const { acceptedFiles, getRootProps, getInputProps } = useDropzone({

maxFiles: 1,

});

const [loading, setLoading] = useState(false);

const { mintMedia } = web3.useContainer();

const router = useRouter();

const { address } = router.query;

const { control, register, handleSubmit, errors } = useForm();

const { fields, append, remove } = useFieldArray({

control,

name: "royalties",

});

const onSubmit = async (data) => {

setLoading(true);

try {

  await mintMedia(

    acceptedFiles[0],

    data.name,

    data.description,

    data.royalty,

    data.royalties

  );

  router.push(`/${address}/success`);

} catch (e) {

  console.log(e);

}

setLoading(false);

};

const onAppend = () => append({ collaborator: “”, shares: “” });

/pages/api/upload.js

import { NFTStorage, Blob } from “nft.storage”;

import { promisify } from “util”;

import fs from “fs”;

import formidable from “formidable”;

const readFileAsync = promisify(fs.readFile);

const handler = async (req, res) => {

const form = new formidable.IncomingForm({ keepExtensions: true });

console.log(“line11”);

const data = await new Promise((res, rej) => {

form.parse(req, (err, fields, files) => {

  if (err) return rej(err);

  console.log("line16");

  res({ fields, files });

  console.log("line18");

});

});

const { metadata, name } = data.fields;

const { media } = data.files;

const mediaData = await readFileAsync(media.path);

console.log(“done reading media”);

if (mediaData && metadata && name) {

const client = new NFTStorage({ token: process.env.NFT_STORAGE_KEY });

const mediaContent = new Blob(mediaData);

const mediaCID = await client.storeBlob(mediaContent);

const metadataContent = new Blob(metadata);

const metadataCID = await client.storeBlob(metadataContent);

res.send({ mediaCID, metadataCID });

} else {

res.status(501);

}

};

export const config = {

api: {

bodyParser: false,

},

};

If that’s not enough info, the whole project can be viewed here.

I really think the mimetype needs to be appended somewhere but I have no clue where to do that.

Frankly if you’re new to coding you’re in way over your head. lol. Which is why guys like me exist, to help the mere mortals. :slight_smile:

Did you debug in your console and at least look at the object you are getting back from ‘storeBlob()’ and verify that it’s the object you expect it to be and has at least the bytes of data in it you expect?

1 Like

Hey thanks for getting back so quickly. Yeah I am in over my head, but I’ve also learned more since starting this project in March than I have from years of fiddling with my computer and a semester of Algorithm Design at community college.

I’ve tried debugging but hasn’t helped me much. Everything seems like it has the right file type except for

const form = new formidable.IncomingForm({ keepExtensions: true });

which returns

uploadjs form
IncomingForm {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
error: null,
ended: false,
maxFields: 1000,
maxFieldsSize: 20971520,
maxFileSize: 209715200,
keepExtensions: true,
uploadDir: ‘C:\Users\Nick\AppData\Local\Temp’,
encoding: ‘utf-8’,
headers: null,
type: null,
hash: false,
multiples: false,
bytesReceived: null,
bytesExpected: null,
_parser: null,
_flushing: 0,
_fieldsSize: 0,
_fileSize: 0,
openedFiles: [],
[Symbol(kCapture)]: false
}

upload.js’ data, data.fields, and data.files all look like this, give or take

data.files

{
media: File {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
size: 905367,
path: ‘C:\Users\Nick\AppData\Local\Temp\upload_a9241d6b760a8ab5d437feb7bb94b2fb.jpg’,
name: ‘Photo_Apr_24,_10_24_58_PM.jpg’,
type: ‘image/jpeg’,
hash: null,
lastModifiedDate: 2021-05-05T04:12:40.019Z,
_writeStream: WriteStream {
_writableState: [WritableState],
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
path: ‘C:\Users\Nick\AppData\Local\Temp\upload_a9241d6b760a8ab5d437feb7bb94b2fb.jpg’,
fd: null,
flags: ‘w’,
mode: 438,
start: undefined,
autoClose: true,
pos: undefined,
bytesWritten: 905367,
closed: false,
[Symbol(kFs)]: [Object],
[Symbol(kCapture)]: false,
[Symbol(kIsPerformingIO)]: false
},
[Symbol(kCapture)]: false
}
}

The console log for what’s in storeBlob() is even less helpful I am afraid.

uploadjs mediaData
<Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 00 01 00 00 ff e2 02 34 49 43 43 5f 50 52 4f 46 49 4c 45 00 01 01 00 00 02 24 61 70 70 6c 04 00 00 00 … 905317 more bytes>
uploadjs mediaContent
Blob {}

Is there any way to examine “Blob{}” more closely? Or get verbose logs? It’s triggered by this line, not sure if there’s any parameters I could add to help my situation…

console.log for blob{}

const mediaData = await readFileAsync(media.path);

console.log(“uploadjs mediaData”);

console.log(mediaData);

if (mediaData && metadata && name) {

const client = new NFTStorage({ token: process.env.NFT_STORAGE_KEY });

const mediaContent = new Blob(mediaData);

const mediaCID = await client.storeBlob(mediaContent);

console.log("uploadjs mediaContent");

console.log(mediaContent);

const metadataContent = new Blob(metadata);

const metadataCID = await client.storeBlob(metadataContent);

res.send({ mediaCID, metadataCID });

} else {

res.status(501);

E2A; All the metadata CID/content works just fine too, IPFS shows something like this:

{“description”:“1”,“mimeType”:“image/png”,“name”:“1”,“version”:“zora-20210101”}

I even uploaded a text document, which shows the broken bytecode(?) instead of just the text, and its metadata mimeType was correct too.

Maybe ‘readFile’ needs the second parameter that’s the encoding?

https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings

I’d bet an image would need the ‘binary’ encoding, but I’m unfamiliar with NodeJs.