Build secure, scalable, and feature-rich file management applications with our comprehensive storage API. From simple file uploads to distributed IPFS storage, from basic SFS to advanced DFS with enterprise features.
allgram Files provides enterprise-level security, GDPR compliance, and seamless integration with popular platforms. Perfect for businesses, developers, and organizations requiring professional file management solutions.
E2EE, FIPS 140-2, GDPR compliant
SFS & DFS, IPFS, chunked uploads
REST API, SDKs, comprehensive docs
Build robust file management applications with Node.js and Express.js. Our comprehensive API provides file upload handling, dual storage support (SFS/DFS), and enterprise-grade security features. Perfect for building scalable backend services and file management applications.
// allgram Files Node.js Integration Example
const express = require('express');
const multer = require('multer');
const crypto = require('crypto');
const axios = require('axios');
const fs = require('fs');
class allgramFilesClient {
constructor(apiKey, baseUrl = 'https://api.allgram.best') {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
this.files = new Map();
this.storageType = 'SFS'; // SFS or DFS
}
// Initialize file storage client
async initializeStorage() {
try {
// Check user subscription for DFS access
const userInfo = await this.getUserInfo();
if (userInfo.isPremium || userInfo.isEnterprise) {
this.storageType = 'DFS';
console.log('✅ DFS storage enabled');
} else {
console.log('✅ SFS storage enabled');
}
} catch (error) {
console.error('Failed to initialize storage:', error);
}
}
// Upload file to SFS (Simple File System)
async uploadToSFS(file, isTemporary = true) {
try {
const formData = new FormData();
formData.append('uploadfiles', file);
const endpoint = isTemporary ? 'file/upload/batch' : 'app-media/upload/batch';
const response = await axios.post(`${this.baseUrl}/v1/${endpoint}`, formData, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'multipart/form-data'
}
});
if (response.status === 200) {
const uploadedFiles = response.data;
uploadedFiles.forEach(fileData => {
this.files.set(fileData.id, { ...fileData, storage: 'SFS' });
});
console.log(`✅ File uploaded to SFS: ${file.name}`);
return uploadedFiles;
}
} catch (error) {
console.error('Failed to upload to SFS:', error.response?.data || error.message);
throw error;
}
}
// Upload file to DFS (Distributed File System) - Premium/Enterprise only
async uploadToDFS(file) {
if (this.storageType !== 'DFS') {
throw new Error('DFS storage requires Premium or Enterprise subscription');
}
try {
const formData = new FormData();
formData.append('uploadfiles', file);
const response = await axios.post(`${this.baseUrl}/v1/file/upload/batch`, formData, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'multipart/form-data'
}
});
if (response.status === 200) {
const uploadedFiles = response.data;
uploadedFiles.forEach(fileData => {
this.files.set(fileData.id, { ...fileData, storage: 'DFS' });
});
console.log(`✅ File uploaded to DFS: ${file.name}`);
return uploadedFiles;
}
} catch (error) {
console.error('Failed to upload to DFS:', error.response?.data || error.message);
throw error;
}
}
// Chunked upload for large files
async uploadLargeFile(file, chunkSize = 2 * 1024 * 1024) { // 2MB chunks
try {
const totalChunks = Math.ceil(file.size / chunkSize);
const chunks = [];
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
}
console.log(`📦 Uploading ${file.name} in ${totalChunks} chunks`);
// Upload chunks sequentially
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', totalChunks);
formData.append('filename', file.name);
const response = await axios.post(`${this.baseUrl}/v1/app-media/upload-chunks/`, formData, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'multipart/form-data'
}
});
if (response.status === 200) {
console.log(`✅ Chunk ${i + 1}/${totalChunks} uploaded`);
}
}
// Complete chunked upload
const completeResponse = await axios.post(`${this.baseUrl}/v1/app-media/convert/upload-chunks/`, {
filename: file.name,
totalChunks: totalChunks
}, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
}
});
if (completeResponse.status === 200) {
console.log(`✅ Large file upload completed: ${file.name}`);
return completeResponse.data;
}
} catch (error) {
console.error('Failed to upload large file:', error.response?.data || error.message);
throw error;
}
}
// Get user files list
async getUserFiles(version = 'v1') {
try {
const endpoint = this.storageType === 'DFS' ? 'files/uploaded' : 'files/uploaded';
const response = await axios.get(`${this.baseUrl}/v1/${endpoint}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
if (response.status === 200) {
const files = response.data;
files.forEach(file => {
this.files.set(file.id, { ...file, storage: this.storageType });
});
console.log(`✅ Retrieved ${files.length} files from ${this.storageType}`);
return files;
}
} catch (error) {
console.error('Failed to get user files:', error.response?.data || error.message);
throw error;
}
}
// Delete file (move to trash for DFS)
async deleteFile(fileId) {
try {
const file = this.files.get(fileId);
if (!file) {
throw new Error('File not found');
}
const endpoint = file.storage === 'DFS' ? 'file/remove' : 'file/remove';
const response = await axios.delete(`${this.baseUrl}/v1/${endpoint}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
},
data: { _id: fileId }
});
if (response.status === 200) {
if (file.storage === 'DFS') {
console.log(`🗑️ File moved to trash: ${file.filename}`);
} else {
this.files.delete(fileId);
console.log(`🗑️ File deleted: ${file.filename}`);
}
return response.data;
}
} catch (error) {
console.error('Failed to delete file:', error.response?.data || error.message);
throw error;
}
}
// Recover file from DFS trash
async recoverFile(fileId) {
if (this.storageType !== 'DFS') {
throw new Error('File recovery only available in DFS storage');
}
try {
const response = await axios.post(`${this.baseUrl}/v1/file/recover`, {
_id: fileId
}, {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
}
});
if (response.status === 200) {
console.log(`✅ File recovered from trash: ${fileId}`);
return response.data;
}
} catch (error) {
console.error('Failed to recover file:', error.response?.data || error.message);
throw error;
}
}
// Get user information
async getUserInfo() {
try {
const response = await axios.get(`${this.baseUrl}/v1/account/info`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
if (response.status === 200) {
return response.data;
}
} catch (error) {
console.error('Failed to get user info:', error.response?.data || error.message);
throw error;
}
}
// Search files by name or type
searchFiles(query) {
const searchTerm = query.toLowerCase();
const results = Array.from(this.files.values()).filter(file =>
file.filename.toLowerCase().includes(searchTerm) ||
file.type_file?.toLowerCase().includes(searchTerm)
);
console.log(`🔍 Found ${results.length} files matching "${query}"`);
return results;
}
// Get file statistics
getFileStats() {
const stats = {
total: this.files.size,
byStorage: {
SFS: 0,
DFS: 0
},
byType: {}
};
this.files.forEach(file => {
stats.byStorage[file.storage]++;
const type = file.type_file || 'unknown';
stats.byType[type] = (stats.byType[type] || 0) + 1;
});
return stats;
}
}
// Usage example
const filesClient = new allgramFilesClient('your-api-key-here');
// Initialize storage
filesClient.initializeStorage();
// Upload file to SFS
const file = fs.createReadStream('./example.jpg');
filesClient.uploadToSFS(file, false)
.then(result => {
console.log('File uploaded successfully:', result);
})
.catch(error => {
console.error('Upload failed:', error);
});
// Upload large file with chunking
const largeFile = fs.createReadStream('./large-video.mp4');
filesClient.uploadLargeFile(largeFile)
.then(result => {
console.log('Large file uploaded successfully:', result);
})
.catch(error => {
console.error('Large file upload failed:', error);
});
Our Node.js integration provides enterprise-grade file management capabilities with minimal setup. Built on proven technologies like Express.js and multer, it offers exceptional performance and reliability for production applications.
Build native iOS file management applications with Swift and SwiftUI. Our iOS SDK provides seamless integration with UIKit and SwiftUI, file operations, and secure storage implementation. Perfect for creating professional iOS file management applications with modern design patterns.
// allgram Files Swift iOS Integration Example
import SwiftUI
import Combine
import CryptoKit
import PhotosUI
// MARK: - File Models
struct FileUpload: Codable, Identifiable {
let id: String
let filename: String
let typeFile: String
let size: Int
let link: String
let storage: String
let creationDatetime: Date
let isSecure: Bool
enum CodingKeys: String, CodingKey {
case id = "_id"
case filename
case typeFile = "type_file"
case size
case link
case storage
case creationDatetime = "creation_datetime"
case isSecure = "is_secure"
}
}
// MARK: - File Service
class allgramFilesService: ObservableObject {
@Published var files: [FileUpload] = []
@Published var isUploading = false
@Published var uploadProgress: Double = 0.0
private let baseURL = "https://api.allgram.best"
private let apiKey: String
private var storageType: String = "SFS"
init(apiKey: String) {
self.apiKey = apiKey
setupFileService()
}
// MARK: - Service Setup
private func setupFileService() {
checkUserSubscription()
}
private func checkUserSubscription() {
Task {
do {
let userInfo = try await getUserInfo()
await MainActor.run {
if userInfo.isPremium || userInfo.isEnterprise {
self.storageType = "DFS"
print("✅ DFS storage enabled")
} else {
print("✅ SFS storage enabled")
}
}
} catch {
print("❌ Failed to check subscription:", error)
}
}
}
// MARK: - File Upload
func uploadFile(_ file: PhotosPickerItem) async throws -> FileUpload {
await MainActor.run {
self.isUploading = true
self.uploadProgress = 0.0
}
do {
let data = try await file.loadTransferable(type: Data.self)
guard let data = data else {
throw FileError.invalidData
}
// Create temporary file
let tempURL = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("tmp")
try data.write(to: tempURL)
// Upload file
let uploadedFile = try await uploadFileToServer(tempURL)
await MainActor.run {
self.files.append(uploadedFile)
self.isUploading = false
self.uploadProgress = 1.0
}
return uploadedFile
} catch {
await MainActor.run {
self.isUploading = false
self.uploadProgress = 0.0
}
throw error
}
}
private func uploadFileToServer(_ fileURL: URL) async throws -> FileUpload {
let endpoint = storageType == "DFS" ? "file/upload/batch" : "app-media/upload/batch"
let url = URL(string: "\(baseURL)/v1/\(endpoint)")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
// Add file data
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name="uploadfiles"; filename="\(fileURL.lastPathComponent)"\r\n".data(using: .utf8)!)
body.append("Content-Type: application/octet-stream\r\n\r\n".data(using: .utf8)!)
body.append(try Data(contentsOf: fileURL))
body.append("\r\n".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw FileError.serverError
}
let uploadedFiles = try JSONDecoder().decode([FileUpload].self, from: data)
return uploadedFiles.first ?? FileUpload(id: "", filename: "", typeFile: "", size: 0, link: "", storage: storageType, creationDatetime: Date(), isSecure: false)
}
// MARK: - File Management
func getUserFiles() async throws {
let endpoint = storageType == "DFS" ? "files/uploaded" : "files/uploaded"
let url = URL(string: "\(baseURL)/v1/\(endpoint)")!
var request = URLRequest(url: url)
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw FileError.serverError
}
let files = try JSONDecoder().decode([FileUpload].self, from: data)
await MainActor.run {
self.files = files
}
}
func deleteFile(_ file: FileUpload) async throws {
let endpoint = file.storage == "DFS" ? "file/remove" : "file/remove"
let url = URL(string: "\(baseURL)/v1/\(endpoint)")!
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = ["_id": file.id]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw FileError.serverError
}
if file.storage == "DFS" {
print("🗑️ File moved to trash: \(file.filename)")
} else {
await MainActor.run {
self.files.removeAll { $0.id == file.id }
}
print("🗑️ File deleted: \(file.filename)")
}
}
// MARK: - Search Files
func searchFiles(query: String) -> [FileUpload] {
let searchTerm = query.lowercased()
return files.filter { file in
file.filename.lowercased().contains(searchTerm) ||
file.typeFile.lowercased().contains(searchTerm)
}
}
// MARK: - Utility Methods
private func getUserInfo() async throws -> UserInfo {
let url = URL(string: "\(baseURL)/v1/account/info")!
var request = URLRequest(url: url)
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw FileError.serverError
}
return try JSONDecoder().decode(UserInfo.self, from: data)
}
}
// MARK: - Supporting Models
struct UserInfo: Codable {
let isPremium: Bool
let isEnterprise: Bool
enum CodingKeys: String, CodingKey {
case isPremium = "is_premium"
case isEnterprise = "is_enterprise"
}
}
enum FileError: Error, LocalizedError {
case invalidData
case serverError
case operationNotSupported
var errorDescription: String? {
switch self {
case .invalidData:
return "Invalid file data"
case .serverError:
return "Server error occurred"
case .operationNotSupported:
return "Operation not supported for current storage type"
}
}
}
// MARK: - SwiftUI Views
struct FilesListView: View {
@StateObject private var filesService = allgramFilesService(apiKey: "your-api-key")
@State private var searchText = ""
@State private var showingFilePicker = false
var body: some View {
NavigationView {
List {
ForEach(filesService.files) { file in
FileRowView(file: file, filesService: filesService)
}
}
.navigationTitle("Files")
.searchable(text: $searchText, prompt: "Search files")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Upload") {
showingFilePicker = true
}
}
}
.photosPicker(isPresented: $showingFilePicker, selection: .constant(nil), matching: .any)
.onAppear {
Task {
try? await filesService.getUserFiles()
}
}
}
}
}
struct FileRowView: View {
let file: FileUpload
let filesService: allgramFilesService
var body: some View {
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(file.filename)
.font(.headline)
Spacer()
if file.isSecure {
Image(systemName: "lock.fill")
.foregroundColor(.green)
}
}
Text(file.typeFile)
.font(.subheadline)
.foregroundColor(.secondary)
Text("Size: \(ByteCountFormatter.string(fromByteCount: Int64(file.size), countStyle: .file))")
.font(.caption)
.foregroundColor(.secondary)
Text("Storage: \(file.storage)")
.font(.caption)
.foregroundColor(.secondary)
}
.padding(.vertical, 4)
.swipeActions {
Button("Delete", role: .destructive) {
Task {
try? await filesService.deleteFile(file)
}
}
}
}
}
Our Swift SDK leverages the latest iOS technologies including SwiftUI, Combine, and async/await. Built with modern iOS development patterns, it provides a seamless developer experience while maintaining high performance and security standards.
Build powerful Android file management applications with Kotlin and Jetpack Compose. Our Android SDK provides seamless integration with modern Android development tools, file operations, and P2P storage capabilities. Perfect for creating professional Android file management applications.
// allgram Files Kotlin Android Integration Example
package com.allgram.files
import android.util.Log
import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.io.File
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
// MARK: - Data Models
data class FileUpload(
val id: String,
val filename: String,
val typeFile: String,
val size: Int,
val link: String,
val storage: String,
val creationDatetime: Long,
val isSecure: Boolean = false
)
data class FileContent(
val text: String? = null,
val media: List<String>? = null,
val poll: String? = null,
val quote: QuoteData? = null
)
data class QuoteData(
val messageId: String,
val content: String,
val author: String
)
// MARK: - File Service
class allgramFilesService(
private val apiKey: String,
private val baseUrl: String = "https://api.allgram.best"
) {
private val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
private var storageType: String = "SFS"
private val fileCallbacks = mutableListOf<(FileUpload) -> Unit>()
private val uploadProgressCallbacks = mutableListOf<(Double) -> Unit>()
// MARK: - Service Initialization
suspend fun initializeStorage() {
try {
val userInfo = getUserInfo()
storageType = if (userInfo.isPremium || userInfo.isEnterprise) "DFS" else "SFS"
Log.d("allgramFiles", "✅ ${storageType} storage enabled")
} catch (e: Exception) {
Log.e("allgramFiles", "Failed to initialize storage", e)
}
}
// MARK: - File Upload
suspend fun uploadFile(file: File, isTemporary: Boolean = true): Result<FileUpload> = withContext(Dispatchers.IO) {
try {
val formData = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"uploadfiles",
file.name,
file.asRequestBody("application/octet-stream".toMediaType())
)
.build()
val endpoint = if (isTemporary) "file/upload/batch" else "app-media/upload/batch"
val request = Request.Builder()
.url("$baseUrl/v1/$endpoint")
.post(formData)
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val fileJson = JSONObject(responseBody ?: "")
val uploadedFile = FileUpload(
id = fileJson.getString("_id"),
filename = fileJson.getString("filename"),
typeFile = fileJson.getString("type_file"),
size = fileJson.optInt("size", 0),
link = fileJson.getString("link"),
storage = storageType,
creationDatetime = fileJson.optLong("creation_datetime", System.currentTimeMillis() / 1000),
isSecure = fileJson.optBoolean("is_secure", false)
)
Log.d("allgramFiles", "✅ File uploaded to $storageType: ${file.name}")
Result.success(uploadedFile)
} else {
Log.e("allgramFiles", "Failed to upload file: ${response.code}")
Result.failure(Exception("Server error: ${response.code}"))
}
} catch (e: Exception) {
Log.e("allgramFiles", "Exception uploading file", e)
Result.failure(e)
}
}
// MARK: - Upload to DFS (Premium/Enterprise only)
suspend fun uploadToDFS(file: File): Result<FileUpload> = withContext(Dispatchers.IO) {
if (storageType != "DFS") {
return Result.failure(Exception("DFS storage requires Premium or Enterprise subscription"))
}
return uploadFile(file, false)
}
// MARK: - Chunked Upload for Large Files
suspend fun uploadLargeFile(file: File, chunkSize: Int = 2 * 1024 * 1024): Result<FileUpload> = withContext(Dispatchers.IO) {
try {
val totalChunks = kotlin.math.ceil(file.length().toDouble() / chunkSize.toDouble()).toInt()
Log.d("allgramFiles", "📦 Uploading ${file.name} in $totalChunks chunks")
// Upload chunks sequentially
for (i in 0 until totalChunks) {
val start = i * chunkSize
val end = minOf(start + chunkSize, file.length().toInt())
val chunk = file.readBytes().slice(start until end)
uploadChunk(chunk, i, totalChunks, file.name)
val progress = (i + 1).toDouble() / totalChunks
uploadProgressCallbacks.forEach { callback ->
callback(progress)
}
}
// Complete chunked upload
val uploadedFile = completeChunkedUpload(file.name, totalChunks)
Log.d("allgramFiles", "✅ Large file upload completed: ${file.name}")
Result.success(uploadedFile)
} catch (e: Exception) {
Log.e("allgramFiles", "Failed to upload large file", e)
Result.failure(e)
}
}
private suspend fun uploadChunk(chunk: ByteArray, index: Int, totalChunks: Int, filename: String) {
val formData = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"chunk",
"chunk_$index",
chunk.toRequestBody("application/octet-stream".toMediaType())
)
.addFormDataPart("chunkIndex", index.toString())
.addFormDataPart("totalChunks", totalChunks.toString())
.addFormDataPart("filename", filename)
.build()
val request = Request.Builder()
.url("$baseUrl/v1/app-media/upload-chunks/")
.post(formData)
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
Log.d("allgramFiles", "✅ Chunk ${index + 1}/$totalChunks uploaded")
} else {
throw Exception("Failed to upload chunk ${index + 1}")
}
}
private suspend fun completeChunkedUpload(filename: String, totalChunks: Int): FileUpload {
val requestBody = JSONObject().apply {
put("filename", filename)
put("totalChunks", totalChunks)
}.toString()
val request = Request.Builder()
.url("$baseUrl/v1/app-media/convert/upload-chunks/")
.post(requestBody.toRequestBody("application/json".toMediaType()))
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val fileJson = JSONObject(responseBody ?: "")
return FileUpload(
id = fileJson.getString("_id"),
filename = fileJson.getString("filename"),
typeFile = fileJson.getString("type_file"),
size = fileJson.optInt("size", 0),
link = fileJson.getString("link"),
storage = storageType,
creationDatetime = fileJson.optLong("creation_datetime", System.currentTimeMillis() / 1000),
isSecure = fileJson.optBoolean("is_secure", false)
)
} else {
throw Exception("Failed to complete chunked upload")
}
}
// MARK: - File Management
suspend fun getUserFiles(): Result<List<FileUpload>> = withContext(Dispatchers.IO) {
try {
val endpoint = if (storageType == "DFS") "files/uploaded" else "files/uploaded"
val request = Request.Builder()
.url("$baseUrl/v1/$endpoint")
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val filesArray = JSONObject(responseBody ?: "").getJSONArray("files")
val files = mutableListOf<FileUpload>()
for (i in 0 until filesArray.length()) {
val fileJson = filesArray.getJSONObject(i)
val file = FileUpload(
id = fileJson.getString("_id"),
filename = fileJson.getString("filename"),
typeFile = fileJson.getString("type_file"),
size = fileJson.optInt("size", 0),
link = fileJson.getString("link"),
storage = storageType,
creationDatetime = fileJson.optLong("creation_datetime", System.currentTimeMillis() / 1000),
isSecure = fileJson.optBoolean("is_secure", false)
)
files.add(file)
}
Log.d("allgramFiles", "✅ Retrieved ${files.size} files from $storageType")
Result.success(files)
} else {
Log.e("allgramFiles", "Failed to get user files: ${response.code}")
Result.failure(Exception("Server error: ${response.code}"))
}
} catch (e: Exception) {
Log.e("allgramFiles", "Exception getting user files", e)
Result.failure(e)
}
}
// MARK: - Delete File
suspend fun deleteFile(file: FileUpload): Result<Unit> = withContext(Dispatchers.IO) {
try {
val endpoint = if (file.storage == "DFS") "file/remove" else "file/remove"
val requestBody = JSONObject().apply {
put("_id", file.id)
}.toString()
val request = Request.Builder()
.url("$baseUrl/v1/$endpoint")
.delete(requestBody.toRequestBody("application/json".toMediaType()))
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
if (file.storage == "DFS") {
Log.d("allgramFiles", "🗑️ File moved to trash: ${file.filename}")
} else {
Log.d("allgramFiles", "🗑️ File deleted: ${file.filename}")
}
Result.success(Unit)
} else {
Log.e("allgramFiles", "Failed to delete file: ${response.code}")
Result.failure(Exception("Server error: ${response.code}"))
}
} catch (e: Exception) {
Log.e("allgramFiles", "Exception deleting file", e)
Result.failure(e)
}
}
// MARK: - Recover File from DFS Trash
suspend fun recoverFile(fileId: String): Result<Unit> = withContext(Dispatchers.IO) {
if (storageType != "DFS") {
return Result.failure(Exception("File recovery only available in DFS storage"))
}
try {
val requestBody = JSONObject().apply {
put("_id", fileId)
}.toString()
val request = Request.Builder()
.url("$baseUrl/v1/file/recover")
.post(requestBody.toRequestBody("application/json".toMediaType()))
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
Log.d("allgramFiles", "✅ File recovered from trash: $fileId")
Result.success(Unit)
} else {
Log.e("allgramFiles", "Failed to recover file: ${response.code}")
Result.failure(Exception("Server error: ${response.code}"))
}
} catch (e: Exception) {
Log.e("allgramFiles", "Exception recovering file", e)
Result.failure(e)
}
}
// MARK: - Search Files
fun searchFiles(files: List<FileUpload>, query: String): List<FileUpload> {
val searchTerm = query.lowercase()
return files.filter { file ->
file.filename.lowercased().contains(searchTerm) ||
file.typeFile.lowercased().contains(searchTerm)
}
}
// MARK: - Utility Methods
private suspend fun getUserInfo(): UserInfo {
val request = Request.Builder()
.url("$baseUrl/v1/account/info")
.addHeader("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val userJson = JSONObject(responseBody ?: "")
return UserInfo(
isPremium = userJson.optBoolean("is_premium", false),
isEnterprise = userJson.optBoolean("is_enterprise", false)
)
} else {
throw Exception("Failed to get user info: ${response.code}")
}
}
// MARK: - Callbacks
fun addFileCallback(callback: (FileUpload) -> Unit) {
fileCallbacks.add(callback)
}
fun addUploadProgressCallback(callback: (Double) -> Unit) {
uploadProgressCallbacks.add(callback)
}
fun removeFileCallback(callback: (FileUpload) -> Unit) {
fileCallbacks.remove(callback)
}
fun removeUploadProgressCallback(callback: (Double) -> Unit) {
uploadProgressCallbacks.remove(callback)
}
}
// MARK: - Supporting Models
data class UserInfo(
val isPremium: Boolean,
val isEnterprise: Boolean
)
// MARK: - ViewModel
class FilesViewModel(
private val filesService: allgramFilesService
) : ViewModel() {
private val _files = MutableStateFlow<List<FileUpload>>(emptyList())
val files: StateFlow<List<FileUpload>> = _files.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
private val _uploadProgress = MutableStateFlow(0.0)
val uploadProgress: StateFlow<Double> = _uploadProgress.asStateFlow()
init {
setupFilesService()
loadFiles()
}
private fun setupFilesService() {
filesService.addUploadProgressCallback { progress ->
_uploadProgress.value = progress
}
}
private fun loadFiles() {
viewModelScope.launch {
_isLoading.value = true
try {
val result = filesService.getUserFiles()
result.onSuccess { files ->
_files.value = files
}.onFailure { error ->
Log.e("FilesViewModel", "Failed to load files", error)
}
} finally {
_isLoading.value = false
}
}
}
fun uploadFile(file: File) {
viewModelScope.launch {
_isLoading.value = true
try {
val result = filesService.uploadFile(file)
result.onSuccess { uploadedFile ->
_files.value = _files.value + uploadedFile
}.onFailure { error ->
Log.e("FilesViewModel", "Failed to upload file", error)
}
} finally {
_isLoading.value = false
}
}
}
fun deleteFile(file: FileUpload) {
viewModelScope.launch {
val result = filesService.deleteFile(file)
result.onSuccess {
if (file.storage != "DFS") {
_files.value = _files.value.filter { it.id != file.id }
}
}.onFailure { error ->
Log.e("FilesViewModel", "Failed to delete file", error)
}
}
}
fun searchFiles(query: String): List<FileUpload> {
return filesService.searchFiles(_files.value, query)
}
}
// MARK: - Compose UI
@Composable
fun FilesListScreen(
viewModel: FilesViewModel = viewModel { FilesViewModel(allgramFilesService("your-api-key")) }
) {
val files by viewModel.files.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
val uploadProgress by viewModel.uploadProgress.collectAsState()
LazyColumn {
items(files) { file ->
FileItem(
file = file,
onDelete = { viewModel.deleteFile(file) }
)
}
}
if (isLoading) {
CircularProgressIndicator()
}
if (uploadProgress > 0.0 && uploadProgress < 1.0) {
LinearProgressIndicator(progress = uploadProgress)
}
}
@Composable
fun FileItem(
file: FileUpload,
onDelete: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = file.filename,
style = MaterialTheme.typography.h6
)
if (file.isSecure) {
Icon(
imageVector = Icons.Default.Lock,
contentDescription = "Secure File",
tint = Color.Green
)
}
}
Text(
text = file.typeFile,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
Text(
text = "Size: ${file.size} bytes",
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
Text(
text = "Storage: ${file.storage}",
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
Button(
onClick = onDelete,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
) {
Text("Delete")
}
}
}
}
Our Kotlin SDK leverages the latest Android technologies including Jetpack Compose, Coroutines, and modern Android architecture patterns. Built with performance and developer experience in mind, it provides a robust foundation for building scalable file management applications.
Ready to take your file management integration to the next level? Explore our comprehensive resources and advanced features to build enterprise-grade file applications with allgram.
Learn about our enterprise security features, GDPR compliance, and FIPS 140-2 certification.
Learn More →Discover advanced storage options including SFS, DFS, IPFS, and enterprise solutions.
Learn More →Join our developer community for support, updates, and collaboration.
Join Community →