0

I have a react redux application and a django server. My django server has an endpoint where I pull a file from s3 and base64 encode the file and return the encoded string. When my redux fetch action is fulfilled I want to download the file without navigating to a new page in my react app. Here is the code:

// DocumentReducer.tsx (+ all necessary imports)

export const getDocument = createAsyncThunk("document/download", async (payload: any) => {
  const {id} = payload
  const response = await instance.get(`tables/document/download/${id}`)
  return response.data
})

const documentSlice = createSlice({
  name: "document",
  initialState: initialState,
  reducers:{},
  extraReducers: (builder) => {
    ...
    builder.addCase(getDocument.fulfilled, (state, action) => {
      const file = action.payload.file
      window.location.href = 'data:application/octet-stream;base64,' + file
      state.status = 'fulfilled'
    })
  } 
})
// ExportComponent.tsx (+ all necessary imports)
export const ExportButton: React.FC<ExportButtonProps> = ({document_id}) => {
  const dispatch = useAppDispatch()
  const handleClick = () => {
    const payload = {id: document_id}
    dispatch(getDocument(payload))
  }
  return (
    <Button variant="contained" size="small" onClick={handleClick}>
      <div className="flex gap-3">
        <p>Export</p>
        <ArrowDownTrayIcon className='w-4 text-white'/>
      </div>
    </Button>
  )
}

On the server side:

# views.py
def encode_document(s3_key):
    """ get document from s3 and base 64 encode the document """
    buffer = BytesIO()
    output_stream = BytesIO()
    S3_CLIENT.download_fileobj("BUCKET_NAME", s3_key, buffer)
    encoded = b64encode(buffer.getvalue()).decode()
    return encoded

class DocumentS3(APIView):
    parser_classes = [JSONParser]
    permission_classes = [IsAuthenticated]

    def get(self, request, document_id):
        document = Document.objects.get(id=document_id)
        encoded = encode_document(document.s3_key)
        print(encoded[:10])
        return Response({"file": encoded}, status=status.HTTP_200_OK)

My server successfully returns the necessary encoded bytes. The download link takes me to a notfound page. Does anyone know how to make this navigation successful in redux?

1 Answers1

0

Don't do window.location.href = in a single page app - the common solution is for example this answer: how to download file in react js - one limitation is that the download needs to be initiated by a user action if I remember correctly (click on a button for example), you can't just start a download in the background out of nowhere.

The problem is not specific to react or redux btw, any single page app runs into the same issue when a download is initiated programmatically.

timotgl
  • 2,865
  • 1
  • 9
  • 19