Downloading a file from Oracle Object Storage using Node.JS
Published: 2023-07-23 21:10:00

We extend the tutorial of [How to download a file in a full-stack React app](https://dev.to/farisdurrani/how-to-download-a-file-in-a-full-stack-react-app-2jkk) to learn how to download a file from Oracle Cloud's Object Storage using OCI's NPM SDK.

## Install the SDK
First, we install the SDK in the backend:
```sh
npm i oci-common oci-sdk
```

Make sure you also have your OCI profile configured with your API key as shown [here](https://dev.to/farisdurrani/setting-up-the-oci-configuration-file-using-api-keys-96c).

## Edit the `index.ts` file in backend

We now edit the `backend/index.ts` file to the following to download the file from Object Storage and send it via HTTP to the client:

```ts
import express, { Request } from "express";
import cors from "cors";
import multer from "multer";
import fs from "fs";
import os = require("oci-objectstorage");
import { ConfigFileAuthenticationDetailsProvider } from "oci-common";

// Create express app
const app = express();

app.use(express.json());
app.use(cors());

app.get("/helloText", (req, res) => {
  res.send({ myResponse: "Hello World!" });
});

const provider = new ConfigFileAuthenticationDetailsProvider(
  "~/.oci/config",
  "DEFAULT"
);

const OCI_BUCKET_NAMESPACE = "id9ek7fershl";
const MAX_OBJECT_SIZE = 1_000_000_000; // 1 GB

const client = new os.ObjectStorageClient({
  authenticationDetailsProvider: provider,
});

app.get(
  "/getObject",
  async (
    req: Request<
      void,
      unknown,
      void,
      { bucketName: string; objectName: string }
    >,
    res
  ) => {
    console.log(`${req.method} ${req.path}`);
    const { bucketName, objectName } = req.query;
    function onFinish(filepath: string, lastModified: Date) {
      console.log("Last modified: ", lastModified);
      res.status(200).download(filepath);
      console.log(
        `Successfully got object ${objectName} from bucket ${bucketName}`
      );
    }
    try {
      if (!bucketName || !objectName) {
        throw new Error("Missing required parameters");
      }
      await getObject({
        bucketName,
        objectName,
        onFinish,
      });
      console.log(`200 ${req.path}`);
    } catch (err) {
      console.error(err);
      res.status(500).send(err);
    }
  }
);

/**
 * Get an object from an OCI bucket
 * @param getObjectDetails
 * @returns the filepath of the temporary object and the last modified date of the object
 */
export async function getObject(getObjectDetails: {
  objectName: string;
  bucketName: string;
  onFinish: (filepath: string, lastModified: Date) => void;
}) {
  const { objectName, bucketName, onFinish } = getObjectDetails;
  console.log(
    `Beginning getting object ${objectName} from bucket ${bucketName}`
  );
  const getObjectRequest: os.requests.GetObjectRequest = {
    objectName: objectName,
    bucketName: bucketName,
    namespaceName: OCI_BUCKET_NAMESPACE,
  };
  let reader: ReadableStreamDefaultReader<any> | undefined;
  try {
    const getObjectResponse = await client.getObject(getObjectRequest);
    const stream = getObjectResponse.value as ReadableStream<any>;
    reader = stream.getReader();
    const downloadFilepath = "./temp/" + objectName;
    const writableStream = fs.createWriteStream(downloadFilepath);

    while (writableStream.bytesWritten < MAX_OBJECT_SIZE) {
      const { done, value } = await reader.read();
      if (done) {
        writableStream.end();
        reader.releaseLock();
        break;
      }
      writableStream.write(value);
    }

    writableStream.on("finish", () => {
      console.log(bucketName);
      onFinish(downloadFilepath, getObjectResponse.lastModified);
    });
  } catch (err) {
    reader?.releaseLock();
    if (err.statusCode === 404) {
      throw new Error(`Object ${objectName} not found in bucket ${bucketName}`);
    }
  }
}

app.listen(8000, () => {
  console.log(`Server is running on port ${8000}`);
});
```

Of course, remember to change the namespace and config profile details to your own. Happy coding!


***Safe harbor statement***
The information provided on this channel/article/story is solely intended for informational purposes and cannot be used as a part of any contractual agreement. The content does not guarantee the delivery of any material, code, or functionality, and should not be the sole basis for making purchasing decisions. The postings on this site are my own and do not necessarily reflect the views or work of Oracle or Mythics, LLC.

*This work is licensed under a* [*Creative Commons Attribution 4.0 International License*](http://creativecommons.org/licenses/by/4.0/)*.*