reactjsmatlabdjango-rest-frameworknext.jsmat

Send .mat file through Django Rest Framework


I have an issue to send the contents of a .mat file to my frontend. My end goal is to allow clients to download the content of this .mat file at the click of a button so that they end up with the same file in their possession. I use Next.js + Django Rest Framework.

My first try was as follow:

class Download(APIView):
     
    def get(self, request):
          with open('file_path.mat', 'rb') as FID:
               fileInstance = FID.read()

          return Response(
              fileInstance,
              status=200,
              content_type="application/octet-stream",
          )

If I print out the fileInstance element I get some binary results:

z\xe1\xfe\xc6\xc6\xd2\x1e_\xda~\xda|\xbf\xb6\x10_\x84\xb5~\xfe\x98\x1e\xdc\x0f\x1a\xee\xe7Y\x9e\xb5\xf5\x83\x9cS\xb3\xb5\xd4\xb7~XK\xaa\xe3\x9c\xed\x07v\xf59Kbn(\x91\x0e\xdb\xbb\xe8\xf5\xc3\xaa\x94Q\x9euQ\x1fx\x08\xf7\x15\x17\xac\xf4\x82\x19\x8e\xc9...

But I can't send it back to my frontend because of a

"UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9c in position 137: invalid start byte"

This error is always the same regardless of which .mat file I try to send in my response.

Next I tried to use the scipy.io.loadmat() method. In this case, fileInstance gives me a much more readable dictionary object, but I still can't get it to transfer to the frontend because of the presence of NaN in my dict:

ValueError: Out of range float values are not JSON compliant

Finally, some suggested to use h5py to send back the data as such:

with h5py.File('file_path.mat', 'r') as fileInstance:
     print(fileInstance)

But in that case the error I get is

Unable to open file (file signature not found)

I know my files are not corrupted because I can open them in Matlab with no problem.

With all this trouble I'm wondering if I'm using the right approach to this problem. I could technically send the dictionary obtained through 'scipy.io.loadmat()' as a str element instead of binary, but I'll have to figure out a way to convert this text back to binary inside a Javascript function. Would anybody have some ideas as to how I should proceed?


Solution

  • The problem was in my frontend after all. Still, here's the correct way to go about it:

    class Download(APIView):
        parser_classes = [FormParser, MultiPartParser]
        def get(self, request):
            try:
                file_path = "xyz.mat"
                response = FileResponse(file_path.open("rb"), content_type="application/octet-stream")
                response["Content-Disposition"] = f"attachment; filename=file_name"
                return response
    
            except Exception as e:
                return Response(status=500)
    

    This should send to the frontend the right file in the right format. No need to worry about encoding and such.

    Meanwhile, on the frontend you should receive the file as follows:

            onClick={() => {
              const url = '/url_to_your_api/';
              axios({ method: 'get', url: url, responseType: 'blob' })
                .then((response) => {
                  const { data } = response;
                  const fileName = 'file_name';
                  const blob = new Blob([data], { type: 'application/octet-stream' });
                  const href = URL.createObjectURL(blob);
                  const link = document.createElement('a');
                  link.href = href;
                  link.download = fileName + '.mat';
                  document.body.appendChild(link);
                  link.click();
                  document.body.removeChild(link);
                  URL.revokeObjectURL(href);
                })
                .catch((response) => {
                  console.error(response);
                });
            }}
    

    Long story short, the part I was missing was to specify to receive the data as blob inside the 'onClick()' function. By default, responseType from Axios is set to Json/String. For that reason, my file was modified at reception and would not be usable in matlab afterwards. If you face a similar problem in the future, try to use the 'shasum' BASH function to observe the hashed value of the file. It is with the help of that function that I could deduce that my API function would return the correct value and that therefore the problem was happenign on the frontend.