How to Upload Images with Retrofit — Android

Narayan Panthi
Nerd For Tech
Published in
11 min readJan 17, 2023

Learn with ActivityResult API, A Standard way to upload any media with a retrofit.

⛵️ Hello, Welcome to Retrofit media uploading tutorial. We will try and simulate how image, audio, document & video are sent from Android to server with retrofit.

What you will learn here?

  • ActivityResult API for Permission & File Provider
  • Showcase picked media like in RecyclerView.
  • Retrofit2 Uploading Way of Multipart/ResponseBody.
  • In context of Messaging App like Slack, Fb Messenger.

Introduction

Retrofit is a popular library for Android and Java which makes it easy to send HTTP requests and handle responses developed by Square. Retrofit is made on the top of OKHTTP Client. With retrofit we can GET, POST, PUT, PATCH, QUERY with web API fast & easily. Also we can upload and retrieve files via REST web service.

implementation 'com.squareup.retrofit2:retrofit:2.9.0'

Let’s get started, assuming you have set up the project & have retrofit implemented. If you are not aware of it you can read it in my profile.

Step 1: Creating UI to Select Image & Show Selected Images

Here, we are going to implement UI where user will able to add multiple media from media picker & showcase it in message editor.

Message Editor UI

Our XML will contains similar elements like above. A RecyclerView for Added Medias, A ImageView for options of media types & EditText to write a message.

<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/editorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">

<View
android:id="@+id/viewOption"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_light_grey" />

<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">

<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/messageEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:ems="16"
android:includeFontPadding="false"
android:inputType="textMultiLine|textNoSuggestions"
android:maxLines="8"
android:focusableInTouchMode="true"
android:cursorVisible="true"
android:minHeight="40dp"
android:paddingStart="@dimen/padding_10dp"
android:paddingTop="@dimen/padding_5dp"
android:paddingEnd="@dimen/padding_10dp"
android:paddingBottom="@dimen/padding_5dp"
android:textSize="15sp"
app:mentionTextColor="@color/color_primary"
android:paddingLeft="@dimen/padding_8dp">
</androidx.appcompat.widget.AppCompatEditText>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_added_media"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:padding="@dimen/padding_8dp"
android:visibility="gone">

</androidx.recyclerview.widget.RecyclerView>

</androidx.appcompat.widget.LinearLayoutCompat>

<View
android:id="@+id/view_divider"
android:layout_width="10dp"
android:layout_height="0.1dp"
android:layout_gravity="center"
android:background="@color/color_light_grey" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_white"
android:paddingStart="@dimen/padding_5dp"
android:paddingEnd="@dimen/padding_5dp">

<FrameLayout
android:layout_width="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_add_other_option"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:maxWidth="@dimen/height_35dp"
android:maxHeight="@dimen/height_35dp"
android:padding="@dimen/padding_8dp"
app:srcCompat="@drawable/ic_plus_circle_16dp" />

</FrameLayout>

<RelativeLayout
android:id="@+id/fl_send_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:paddingStart="@dimen/padding_10dp"
android:paddingTop="@dimen/padding_2dp"
android:paddingBottom="@dimen/padding_2dp"
android:layout_alignParentRight="true"
android:paddingLeft="@dimen/padding_10dp">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/sendImageView"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center"
android:layout_marginStart="@dimen/margin_2dp"
android:layout_marginTop="@dimen/margin_2dp"
android:layout_marginEnd="@dimen/margin_2dp"
android:layout_marginBottom="@dimen/margin_2dp"
android:background="@drawable/ic_send_background"
android:padding="@dimen/padding_8dp"
android:visibility="visible"
app:srcCompat="@drawable/ic_send" />
</RelativeLayout>
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

Now, Let’s create a MediaPreviewModel to display added media in RecyclerView also to save details of picked media like URI, type, size, mime type, duration for audio/video etc.


public class MediaPreviewModel implements Parcelable {

public enum MediaPickedType {
TYPE_CAMERA,
TYPE_PHOTOS,
TYPE_FILE,
TYPE_AUDIO,
TYPE_VIDEO,
TYPE_RECORDED,
TYPE_NOT_ACCEPTED
}
private String mediaId;

//file uri
private String uri;

// uploading with api on selected
private String uploadedUrlFromApi;

private String fileName;
private long fileSize;
private long duration;
private String fileMimeType;

// check if video is recorded
// recorded video directly stores in cache file
private boolean isRecordedVideo = false;

// uploading with api on selected
private boolean isUploaded = false;

private boolean isFailed = false;
private boolean isUploading = false;

private MediaPickedType mediaPickedType;

//... getter setter
}

MediaPreviewModel a placeholder model for added media, which will also help us to add/remove picked media before uploading it to server.

Now, Let’s create AddedMediaAdapter to showcase picked medias, Considering we have 3 view-types in RecyclerView, which layout may vary with respect to their media types.

Added media types UI from Slack.
public class AddedMediaAdapter extends RecyclerView.Adapter {

List<MediaPreviewModel> mediaPreviewModelList;
private final int TYPE_IMAGE = 0;
private final int TYPE_FILE = 1;
private final int TYPE_AUDIO = 2;

public void setData(List<MediaPreviewModel> mediaPreviewModelList) {
this.mediaPreviewModelList = mediaPreviewModelList;
this.notifyDataSetChanged();
}
// onCreateViewHolder for view TYPES
....
@Override
public int getItemViewType(int position) {
if (mediaPreviewModelList.get(position).getMediaPickedType().equals(MediaPreviewModel.MediaPickedType.TYPE_PHOTOS) || mediaPreviewModelList.get(position).getMediaPickedType().equals(MediaPreviewModel.MediaPickedType.TYPE_CAMERA)) {
return TYPE_IMAGE;
}else if (mediaPreviewModelList.get(position).getMediaPickedType().equals(MediaPreviewModel.MediaPickedType.TYPE_AUDIO)){
return TYPE_AUDIO;
}
return TYPE_FILE;
}

// create necessary view holder class for image, audio, videos

public void deletePhotoFromList(MediaPreviewModel mediaPreviewModel) {
mediaPreviewModelList.remove(mediaPreviewModel);
int indexOfAddedMediaMode = mediaPreviewModelList.indexOf(mediaPreviewModel);
notifyItemRemoved(indexOfAddedMediaMode);
notifyDataSetChanged();
}

public void clear() {
if (mediaPreviewModelList != null) {
mediaPreviewModelList.clear();
notifyDataSetChanged();
}
}

// for user actions
public interface OnItemActionClickListener {
void onAddedItemDelete(MediaPreviewModel mediaPreviewModel, int addedImagesSize, int position);
void onAddedItemItemClick(MediaPreviewModel mediaPreviewModel, int position);
}

public void setOnItemActionListener(OnItemActionClickListener listener) {
this.onItemActionClickListener = listener;
}
}

Then, setup AddedMediaAdapter in RecyclerView,

private void doMediaSetupWork() {
addedMediaAdapter = new AddedMediaAdapter(requireContext());
LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false);
getViewDataBinding().parentEditor.rvMedia.setLayoutManager(layoutManager);
getViewDataBinding().parentEditor.rvMedia.setHasFixedSize(true);
mediaAdapterDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
getViewModel().isShowSendMessageIcon.postValue(addedMediaAdapter != null && addedMediaAdapter.getItemCount() > 0);
super.onChanged();
}
};
addedMediaAdapter.registerAdapterDataObserver(mediaAdapterDataObserver);

// on delete image
addedMediaAdapter.setOnItemDeleteListener(new AddedMediaAdapter.OnItemActionClickListener() {
@Override
public void onAddedItemDelete(MediaPreviewModel mediaPreviewModel, int addedImagesSize, int position) {
addedMediaAdapter.deletePhotoFromList(mediaPreviewModel);
addMediaPreviewModelList.remove(mediaPreviewModel);
if (addedMediaAdapter.getItemCount() == 0) {
...
}
// delete picked media if user removes
}

@Override
public void onAddedItemItemClick(MediaPreviewModel mediaPreviewModel, int position) {
switch (mediaPreviewModel.getMediaPickedType()) {
...
// open preview details for picked media
}
}
});

Recently, Android have introduce new Photo Picker which is nice,

But Here, we will use FileProvider which is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content://Uri for a file instead of a file:///Uri.

Let’s start with Image, A user can click picture right-away from camera or can picked already captured photos. So, we separate these two action with

  1. For CAMERA — to capture image from camera.
  2. For PHOTOS — to pick gallery images.

For current case we are saving files temporarily in local app-specific folder where path of gallery media differs from it.

Defining a FileProvider

Add a <provider> element to your app manifest.

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />

</provider>

Specifying Available Files

<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path
name="cached_files"
path="." />
<files-path
name="images"
path="." />
<external-files-path
name="my_images"
path="Pictures" />
</paths>

Now, Let’s move to the part where user choose to add media, Suppose there is click events for Camera Capture, Gallery, File picker etc. We will start with permission check.

Step 2: Permissions For Camera & Storage

To capture or pick pictures we need Runtime permission,

OnActivityResult method is deprecated. The new way looks unnecessarily complicated compared to the old one, but it’s simple & clean.

Case 1 : For CAMERA

CAMERA_PERMISSION = Manifest.permission.CAMERA;

We will need camera access permission to capture image from camera, Let’s create ActivityResultLauncher which will do necessary permission check.

// activity result for camera permission
private final ActivityResultLauncher<String> mPermissionResult = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result) {
doImagePickFromCamera();
} else {
openAppSettings();
}
});

Then, we can trigger permission check by

ivCameraImage.setOnClickListener(v -> mPermissionResult.launch(CAMERA_PERMISSION));

After permission is granted,

// create temp file for image captured from camera
private void doImagePickFromCamera() {
tempFileName = String.valueOf(System.currentTimeMillis());
tempCaptureImageUri = FileUtils.getTempFileUri(requireContext(), tempFileName);
takeImage.launch(tempCaptureImageUri);
}
public static Uri getTempFileUri(Context context, String tempFileName) {
File tempFile = null;
try {
tempFile = File.createTempFile(tempFileName, ".jpg", context.getCacheDir());
tempFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
assert tempFile != null;
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", tempFile);
}

We can get captured result from camera in onActivityResult.

//  take picture from camera
ActivityResultLauncher<Uri> takeImage = registerForActivityResult(new ActivityResultContracts.TakePicture(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if (result) {
MediaPreviewModel mediaPreviewModel = new MediaPreviewModel();
mediaPreviewModel.setMediaId(tempFileName);
mediaPreviewModel.setUri(tempCaptureImageUri);
mediaPreviewModel.setUploaded(false);
mediaPreviewModel.setFailed(false);
mediaPreviewModel.setMediaPickedType(MediaPreviewModel.MediaPickedType.TYPE_CAMERA);
addMediaPreviewModelList.add(mediaPreviewModel);
addedMediaAdapter.setData(addMediaPreviewModelList);
}
}
});

Case 2: For PHOTOS

STORAGE_PERMISSION = Manifest.permission.READ_EXTERNAL_STORAGE;

We will need storage permission to access images from gallery

// activity result storage read permission for photos
private final ActivityResultLauncher<String> mPermissionStorageRead = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result) {
doImagePickFromStorage();
} else {
openAppSettings();
}
});

Then , we can trigger storage permission by

ivGalleryImage.setOnClickListener(v -> mPermissionStorageRead.launch(STORAGE_PERMISSION));

If permission is granted, we can get multiple contents from media picker.

// create image content pick request
private void doImagePickFromStorage() {
mGetMultipleContent.launch("image/*");
}

Here, we get List<Uri> results,

//  get image from gallery
ActivityResultLauncher<String> mGetMultipleContent = registerForActivityResult(new ActivityResultContracts.GetMultipleContents(),
new ActivityResultCallback<>() {
@Override
public void onActivityResult(List<Uri> results) {
if (results != null) {
if (results.size() > 0) {
for (Uri resultItem : results) {
MediaPreviewModel mediaPreviewModel = new MediaPreviewModel();
mediaPreviewModel.setMediaId(UUID.randomUUID().toString().replace("-", ""));
mediaPreviewModel.setUri(resultItem);
mediaPreviewModel.setUploaded(false);
mediaPreviewModel.setFailed(false);
mediaPreviewModel.setMediaPickedType(MediaPreviewModel.MediaPickedType.TYPE_PHOTOS);
addMediaPreviewModelList.add(mediaPreviewModel);
}
addedMediaAdapter.setData(addMediaPreviewModelList);
}
}
}
});

Now, Added media preview model list is filled with medias. It’s time to upload these images.

Step 3: Creating Retrofit API Service.

To send files and data to an HTTP server we need to make Multipart request. While working with Multipart request you need to adjust it depending on the server endpoint and requirements.

You need to identify how files/images are handled on server side, by checking your API url in postman or API documentation. It may be with single key “images” or ”file” or with array keys “images []” for multiple images or other formats like Base64, depending on how API request is created.

Using Retrofit 2, you need to use either OkHttp’s RequestBody or MultipartBody.Part classes and encapsulate your file into a request body.

Case 1: Uploading with RequestBody

  • If API is accepting its requests in RequestBody.
@POST("upload/images")
Observable<YourResponseObject>
uploadImages(@Body RequestBody imagesBody);

Case 2: Uploading with Multipart

  • If API is accepting its requests in a MultipartBody body.
@Multipart
@POST(upload/file)
Observable<YourResponseObject>
uploadFile(@Part MultipartBody.Part partFile);

To create these Multipart/ ResponseBody we need to verify with endpoints accepting parameter and request it by creating it as follows.

public static MultipartBody buildMultipartForMultipleImages(Context context, List<MediaPreviewModel> allAddMediaImages) {

MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);

for (int index = 0; index < allAddMediaImages.size(); index++) {
if (allAddMediaImages.get(index).getMediaPickedType().equals(MediaPreviewModel.MediaPickedType.TYPE_CAMERA)) {
builder.addFormDataPart("images", "image" + index, getRequestBodyForCameraImages(context, allAddMediaImages.get(index)));
} else {
builder.addFormDataPart("images", "image" + index, getRequestBodyForFolderImages(context, allAddMediaImages.get(index).getUri()));
}
}
return builder.build();

}

Getting Request Body from Camera Images, When capturing image from camera we may need to fix rotation of the images (optional) if in-case your images are captured in different rotation.

// creating multipart from camera
public static RequestBody getRequestBodyForCameraImages(Context context, MediaPreviewModel mediaPreviewModel) {

File file = new File(context.getCacheDir(), getFileName(context, mediaPreviewModel.getUri()));
Bitmap decodedBitmap = null;
try {
decodedBitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), mediaPreviewModel.getUri());
} catch (IOException e) {
e.printStackTrace();
}
Bitmap bitmap = null;
RequestBody imageReqBody;
try {
bitmap = fixBitmapRotation(mediaPreviewModel.getUri(), decodedBitmap, context);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap != null) {
byte[] byteArray = getByteArrayFromBitmap(bitmap);
imageReqBody = RequestBody.create(byteArray, MediaType.parse("image/jpg"));
} else {
assert decodedBitmap != null;
byte[] byteArray = getByteArrayFromBitmap(decodedBitmap);
imageReqBody = RequestBody.create(byteArray, MediaType.parse("image/jpg"));

}
return imageReqBody;
}

public static String getFileName(Context context, Uri uri) {
String result = null;
String scheme = uri.getScheme();
if (scheme != null && scheme.equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
assert cursor != null;
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}

public static Bitmap fixBitmapRotation(Uri uri, Bitmap bitmap, Context context) throws IOException {
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23) {
InputStream input = context.getContentResolver().openInputStream(uri);
assert input != null;
ei = new ExifInterface(input);
} else {
ei = new ExifInterface(Objects.requireNonNull(uri.getPath()));
}
int rotation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix matrix = new Matrix();
if (rotation != 0) {
matrix.postRotate(rotationInDegrees);
}

return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}

private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}

public static byte[] getByteArrayFromBitmap(Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.WEBP, 80, stream);
return stream.toByteArray();
}

Getting Request Body from Folder Image,

// creating multipart from photos
public static RequestBody getRequestBodyForFolderImages(Context context, Uri uri) {
File file = null;
try {
file = getFile(context, uri);
} catch (IOException e) {
e.printStackTrace();
}
// if you need to compressed image before sending (optional)
// File file1 = CompressFileUtil.getCompressedImageFile(file, context);
RequestBody imageReqBody;
if (file != null) {
imageReqBody = RequestBody.create(file, MediaType.parse("image/jpg"));
} else {
assert file != null;
imageReqBody = RequestBody.create(file, MediaType.parse("image/jpg"));
}
return imageReqBody;
}

public static File getFile(Context context, Uri uri) throws IOException {
File destinationFilename;
try {
destinationFilename = new File(context.getFilesDir().getPath() + File.separatorChar + queryName(context, uri));
} catch (AssertionError e) {
destinationFilename = new File(uri.getPath());
}


try (InputStream ins = context.getContentResolver().openInputStream(uri)) {
createFileFromStream(ins, destinationFilename);
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
return destinationFilename;
}

public static void createFileFromStream(InputStream ins, File destination) {
try (OutputStream os = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
int length;
while ((length = ins.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
}

private static String queryName(Context context, Uri uri) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
assert returnCursor != null;
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String name = returnCursor.getString(nameIndex);
returnCursor.close();
return name;
}

Now you can request your api with these parameter’s and upload images with retrofit.

Leaving here’s a sample code for uploading files with retrofit (RxJava).

// Api service for Uploading documents
@Multipart
@POST(ApiEndPoints.UPLOAD_DOC)
Observable<YourResponseObject>
uploadDocument(@Part MultipartBody.Part partFile);
public static MultipartBody.Part buildMultipartForFiles(Context context, MediaPreviewModel mediaPreviewModel) {
File file = null;
try {
file = getFile(context, mediaPreviewModel.getUri());
} catch (IOException e) {
e.printStackTrace();
}
assert file != null;
RequestBody imageReqBody = RequestBody.create(file, MediaType.parse(mediaPreviewModel.getFileMimeType()));
return MultipartBody.Part.createFormData("doc", file.getName(), imageReqBody);
}
addSubscribe(conversationMessageRepository
.uploadDocument(addedFileMultipart)
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribeWith(new DisposableObserver<BaseResponse>() {
@Override
public void onNext(BaseResponse baseResponse) {
if (baseResponse.getSuccess()) {
String fileUrl = baseResponse.getFileUrl();
// here's your uploaded file url

} else {
// todo error handling work.
}
}

@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
// todo error handling work.
AppLog.showLog("File uploading failed..." + e.getMessage());
}

@Override
public void onComplete() {

}
}));

Finally done, Now you can upload any media files with retrofit.

A tip for Messaging App Programmers, You can save this media preview model in database with your messaging data to notify about sending media while media is being upload to server for instant media sending behavior.

At the End

How Messaging Apps are communicating & working with Media?

Real Time Communication (RTC) refers to the ability to transmit and receive data or information immediately and without delay. This can include technologies such as live chat, video conferencing, and instant messaging. Real-time communication is often used in applications where time-sensitive information needs to be exchanged, such as in online gaming, stock trading, and emergency response situations.

Real-time communication chat in Android can be implemented using various techniques, including using web sockets or long polling. Other libraries and frameworks such as SignalR, XMPP, and MQTT can also be used to build real-time chat functionality in android apps.

Slack uses a combination of technologies to provide real-time communication functionality. One of the key technologies used by Slack is WebSockets. Slack also uses their own proprietary real-time messaging protocol called RTM (Real Time Messaging) which provides low-latency, bi-directional communication between clients and servers. RTM provides the ability to receive and send messages, plus other functionality such as presence notifications and typing indicators, which are essential for a real-time chat app.

Facebook Messenger uses a combination of technologies like WebSockets, and MQTT (MQ Telemetry Transport) which is a machine-to-machine (M2M)/”Internet of Things” connectivity protocol. MQTT is a lightweight publish/subscribe messaging transport that is designed for connections with remote locations where a “small code footprint” is required or the network bandwidth is limited. MQTT was designed to be used on the top of TCP/IP protocol so that create a reliable connection and allow the transmission of data with low network overhead.

Generally, All Messaging overlay with Socket based communication, In case of media files, All files are stored in their server, It can be different server for handling media files which require more powerful server to read/write chunks of data.

One common approach is to use a separate server for hosting the media files, and then reference the location of the media file in the message payload.

For example, you could have a media server that is responsible for hosting and serving the media files, and an MQTT server that handles the real-time messaging. The MQTT server would then send messages containing the location of the media file (such as a URL) as the payload, and the client would then use that information to request the media file from the media server.

Another approach is to use a CDN (Content Delivery Network) for hosting the media files, which can be accessed by the clients via HTTP or HTTPS. In this case, the MQTT server would send the CDN URL in the message payload and the client would download the media file from the CDN.

That’s all thank you for reading.

Good bye & Take care..

--

--