2

So I and my team are trying really hard to upload and retrieve SVG to firebase storage using cloud functions. With the function that we have built, we are able to upload any image we want except for SVG. For some reason SVG is not working I don't know if we are encoding it wrong or something

Here is my function file

const admin = require("firebase-admin");
const { nanoid } = require("nanoid");
const mime = require("mime-types");
const { validateCauseImageFile } = require("./fileTypeUtils");
const { imageStorageUrlMatch } = require("./regularExpression");

module.exports.imageValidationAndUpload = async (image) => {
  try {
    console.log("Image in base 64", image);
    const bucket = admin.storage().bucket();
    const mimeType = image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)
      ? image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)[1]
      : null;
    const fileExtension = mime.extension(mimeType);
    console.log(fileExtension);
    const fileName = nanoid() + `.${fileExtension}`;
    console.log("Mime type", mimeType);
    const base64EncodedImageString = image.replace(
      /^data:image\/\w+;base64,/,
      ""
    );
    const imageBuffer = Buffer.from(base64EncodedImageString, "base64");
    const result = await validateCauseImageFile(imageBuffer.length, mimeType);

    if (result.success) {
      const token = nanoid();
      const options = {
        gzip: true,
        metadata: {
          contentType: mimeType,
          metadata: {
            firebaseStorageDownloadTokens: token,
          },
        },
      };
      const filePath = "testSvg/" + fileName;
      const file = bucket.file(filePath);
      await file.save(imageBuffer, options);
      const response = await file.getSignedUrl({
        action: "read",
        expires: "03-17-2025", // this is an arbitrary date
      });
      return { success: true, response: response[0] };

      //   return response;
    } else if (image.match(imageStorageUrlMatch)) {
      return { success: true, url: image };
    } else {
      const errorObject = { status: 403, message: result.message };
      throw errorObject;
    }
  } catch (err) {
    console.log("show error message : ", err.message);
    return { success: false, message: err.message };
  }
};

If I upload a png/jpeg/jpg image then it will be uploaded and work fine but for SVG it will be uploaded but won't render anything. On the firebase storage console bucket this is how it shows up

For SVG image

enter image description here

For png or any other image

enter image description here

The upload process works fine but SVG is not rendering or being retrieved properly. Here are the URL's for both files

SVG: https://firebasestorage.googleapis.com/v0/b/infaque-playground.appspot.com/o/testSvg%2FaxOmCbg7IY6Q8q8P-5c6D.svg?alt=media&token=6UXNtvtP_ONzQTkmbWypD

PNG: https://firebasestorage.googleapis.com/v0/b/infaque-playground.appspot.com/o/testSvg%2FsbbcDN_8zIpWLfuDyWEYw.png?alt=media&token=I_YsAVHXC3eMIrdGmIiPY

Sulman Azhar
  • 977
  • 1
  • 9
  • 26
  • You may have a look at the [Stackoverflow case](https://stackoverflow.com/questions/69063527/what-is-the-best-way-to-save-an-svg-in-firebase). Let me know if that helps! – Mousumi Roy Dec 22 '21 at 10:52
  • This answer is for java and i don't know about those Bytes, Base64 libraries. Also if i upload image using the upload files button the firebase storage then it works fine and gives me a preview of svg and any other file but when i upload it using functions where i have to convert it to base64 and then store it – Sulman Azhar Dec 23 '21 at 05:43
  • You may refer to a [Stackoverflow case](https://stackoverflow.com/questions/57886005/how-to-decode-base64-to-image-in-nodejs) and the [documentation](https://dev.to/dnature/convert-a-base64-data-into-an-image-in-node-js-3f88) which mentions about decoding base64 to image in Node.js. – Mousumi Roy Dec 23 '21 at 09:22
  • Sorry but no help at all. Can any please help me with this – Sulman Azhar Dec 27 '21 at 11:32
  • Please, could you post the actual SVG image `data` URL you are testing with in the question? Please, be aware that if you defined your image as `data:image/svg+xml;base64...`, i.e., with content-type `image/svg+xml` - as it should be, by the way - the regular expression you used for obtaining the base64 data, `/^data:image\/\w+;base64,/` will not work properly because of the `+` sign - please, try something like `/^data:image\/(\w|\+)+;base64,/` instead. It may be the cause of the problem. – jccampanero Dec 27 '21 at 22:50
  • Could you confirm if the converted file is valid if viewed after downloading it, i.e. in Chrome or Firefox? Could you confirm if the converted file is of type metadata/ binary? – Mousumi Roy Dec 28 '21 at 06:50
  • The conversion process is the same for every image. Also, this is how the SVG image looks on the console https://i.imgur.com/EMnFaWS.png. And the link for this image is this: https://firebasestorage.googleapis.com/v0/b/infaque-playground.appspot.com/o/testSvg%2FJ5LmQ2LeZgfmfTHJnNx2l.svg?alt=media&token=MmglZIw9egQiOIjfnxVDc Also i send base64 string from front and its being recieved okay on the functions i checked it using functions logs – Sulman Azhar Dec 28 '21 at 10:05
  • Thank you for the feedback @SulmanAzhar. I posted an answer trying to further explain my point. I hope it helps. – jccampanero Dec 28 '21 at 13:34

1 Answers1

2

According to your screenshot, the SVG image is transferred as Content-Type image/svg+xml.

According to your code, this content type is calculated as follows:

const mimeType = image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)
      ? image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)[1]
      : null;

It means that your image data URL should be something like this:

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEwMCAxMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiIGhlaWdodD0iMTAwcHgiIHdpZHRoPSIxMDBweCI+CjxnPgoJPHBhdGggZD0iTTI4LjEsMzYuNmM0LjYsMS45LDEyLjIsMS42LDIwLjksMS4xYzguOS0wLjQsMTktMC45LDI4LjksMC45YzYuMywxLjIsMTEuOSwzLjEsMTYuOCw2Yy0xLjUtMTIuMi03LjktMjMuNy0xOC42LTMxLjMgICBjLTQuOS0wLjItOS45LDAuMy0xNC44LDEuNEM0Ny44LDE3LjksMzYuMiwyNS42LDI4LjEsMzYuNnoiLz4KCTxwYXRoIGQ9Ik03MC4zLDkuOEM1Ny41LDMuNCw0Mi44LDMuNiwzMC41LDkuNWMtMyw2LTguNCwxOS42LTUuMywyNC45YzguNi0xMS43LDIwLjktMTkuOCwzNS4yLTIzLjFDNjMuNywxMC41LDY3LDEwLDcwLjMsOS44eiIvPgoJPHBhdGggZD0iTTE2LjUsNTEuM2MwLjYtMS43LDEuMi0zLjQsMi01LjFjLTMuOC0zLjQtNy41LTctMTEtMTAuOGMtMi4xLDYuMS0yLjgsMTIuNS0yLjMsMTguN0M5LjYsNTEuMSwxMy40LDUwLjIsMTYuNSw1MS4zeiIvPgoJPHBhdGggZD0iTTksMzEuNmMzLjUsMy45LDcuMiw3LjYsMTEuMSwxMS4xYzAuOC0xLjYsMS43LTMuMSwyLjYtNC42YzAuMS0wLjIsMC4zLTAuNCwwLjQtMC42Yy0yLjktMy4zLTMuMS05LjItMC42LTE3LjYgICBjMC44LTIuNywxLjgtNS4zLDIuNy03LjRjLTUuMiwzLjQtOS44LDgtMTMuMywxMy43QzEwLjgsMjcuOSw5LjgsMjkuNyw5LDMxLjZ6Ii8+Cgk8cGF0aCBkPSJNMTUuNCw1NC43Yy0yLjYtMS02LjEsMC43LTkuNywzLjRjMS4yLDYuNiwzLjksMTMsOCwxOC41QzEzLDY5LjMsMTMuNSw2MS44LDE1LjQsNTQuN3oiLz4KCTxwYXRoIGQ9Ik0zOS44LDU3LjZDNTQuMyw2Ni43LDcwLDczLDg2LjUsNzYuNGMwLjYtMC44LDEuMS0xLjYsMS43LTIuNWM0LjgtNy43LDctMTYuMyw2LjgtMjQuOGMtMTMuOC05LjMtMzEuMy04LjQtNDUuOC03LjcgICBjLTkuNSwwLjUtMTcuOCwwLjktMjMuMi0xLjdjLTAuMSwwLjEtMC4yLDAuMy0wLjMsMC40Yy0xLDEuNy0yLDMuNC0yLjksNS4xQzI4LjIsNDkuNywzMy44LDUzLjksMzkuOCw1Ny42eiIvPgoJPHBhdGggZD0iTTI2LjIsODguMmMzLjMsMiw2LjcsMy42LDEwLjIsNC43Yy0zLjUtNi4yLTYuMy0xMi42LTguOC0xOC41Yy0zLjEtNy4yLTUuOC0xMy41LTktMTcuMmMtMS45LDgtMiwxNi40LTAuMywyNC43ICAgQzIwLjYsODQuMiwyMy4yLDg2LjMsMjYuMiw4OC4yeiIvPgoJPHBhdGggZD0iTTMwLjksNzNjMi45LDYuOCw2LjEsMTQuNCwxMC41LDIxLjJjMTUuNiwzLDMyLTIuMyw0Mi42LTE0LjZDNjcuNyw3Niw1Mi4yLDY5LjYsMzcuOSw2MC43QzMyLDU3LDI2LjUsNTMsMjEuMyw0OC42ICAgYy0wLjYsMS41LTEuMiwzLTEuNyw0LjZDMjQuMSw1Ny4xLDI3LjMsNjQuNSwzMC45LDczeiIvPgo8L2c+Cjwvc3ZnPg==

And that pattern will not be processed appropriately by the code you are using because of the + sign:

const base64EncodedImageString = image.replace(
      /^data:image\/\w+;base64,/,
      ""
    );

Please, see how the result actually contains the data:image/svg+xml;base64, information as prefix:

image processing

In order to successfully handle the SVG use case, you could modify the regular expression to this:

const base64EncodedImageString = image.replace(
      /^data:image\/(\w|\+)+;base64,/,
      ""
    );

Please, note how now the base64 is successfully extracted:

image processing with modified regex

If the content type is not set like image/svg+xml, please, consider change it to that value, it could be of help as well.

jccampanero
  • 50,989
  • 3
  • 20
  • 49