react-native-my-uploader-android 1.0.55 → 1.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,6 +44,37 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
44
44
  private const val E_INVALID_OPTIONS = "E_INVALID_OPTIONS"
45
45
  }
46
46
 
47
+ private fun getWorkFile(rawUri: String): File {
48
+ val uri = Uri.parse(rawUri)
49
+ val scheme = uri.scheme?.lowercase() ?: ""
50
+ val tempFile = File.createTempFile("work_process_", ".tmp", reactContext.cacheDir)
51
+
52
+ try {
53
+ if (scheme == "http" || scheme == "https") {
54
+ // Uzak URL'den indir
55
+ val url = java.net.URL(rawUri)
56
+ val connection = url.openConnection() as java.net.HttpURLConnection
57
+ connection.connectTimeout = 15000
58
+ connection.readTimeout = 20000
59
+ connection.inputStream.use { input ->
60
+ tempFile.outputStream().use { output -> input.copyTo(output) }
61
+ }
62
+ connection.disconnect()
63
+ } else {
64
+ // Yerel URI'den (content:// veya file://) kopyala
65
+ reactContext.contentResolver.openInputStream(uri).use { input ->
66
+ if (input == null) throw Exception("Dosya açılamadı: $rawUri")
67
+ tempFile.outputStream().use { output -> input.copyTo(output) }
68
+ }
69
+ }
70
+ } catch (e: Exception) {
71
+ tempFile.delete()
72
+ throw e
73
+ }
74
+ return tempFile
75
+ }
76
+
77
+
47
78
  @ReactMethod
48
79
  fun openDocument(options: ReadableMap, promise: Promise) {
49
80
  if (pickerPromise != null) {
@@ -212,90 +243,51 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
212
243
  }
213
244
 
214
245
  @ReactMethod
215
- fun RotateImageWithUri(fileUri: String, angle: Double, promise: Promise) {
216
- Thread {
217
- try {
218
- val sourceUri = Uri.parse(fileUri)
219
- val contentResolver = reactContext.contentResolver
220
-
221
- // 1. ADIM: OOM Riskini Azaltmak için Bitmap'i verimli bir şekilde oku
222
- // (Bu kısım, cropImageWithUri'deki ile aynı mantığı kullanır)
223
- val boundsOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true }
224
- contentResolver.openInputStream(sourceUri)?.use {
225
- BitmapFactory.decodeStream(it, null, boundsOptions)
226
- }
227
-
228
- val decodeOptions = BitmapFactory.Options()
229
- var inSampleSize = 1
230
- val maxDimension = 2048 // Uygulamanızın ihtiyacına göre ayarlayın
231
- if (boundsOptions.outHeight > maxDimension || boundsOptions.outWidth > maxDimension) {
232
- val halfHeight = boundsOptions.outHeight / 2
233
- val halfWidth = boundsOptions.outWidth / 2
234
- while ((halfHeight / inSampleSize) >= maxDimension && (halfWidth / inSampleSize) >= maxDimension) {
235
- inSampleSize *= 2
236
- }
237
- }
238
- decodeOptions.inSampleSize = inSampleSize
239
-
240
- // Resmi optimize edilmiş boyutta belleğe yükle
241
- val originalBitmap = contentResolver.openInputStream(sourceUri)?.use {
242
- BitmapFactory.decodeStream(it, null, decodeOptions)
243
- } ?: throw Exception("Bitmap oluşturulamadı (decodeStream).")
244
-
245
-
246
- // 2. ADIM: Döndürme Matrisini Oluştur
247
- // NOT: Bu fonksiyonda, JS'den gelen 'angle' değerini doğrudan kullanıyoruz.
248
- // EXIF döndürmesi zaten orijinal resimde mevcuttur ve burada tekrar uygulanmamalıdır,
249
- // çünkü amacımız kullanıcının isteği doğrultusunda ek bir döndürme yapmaktır.
250
- val matrix = Matrix().apply {
251
- postRotate(angle.toFloat())
252
- }
253
-
254
- // Döndürme işlemini uygula
255
- val rotatedBitmap = Bitmap.createBitmap(
256
- originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true
257
- )
258
-
259
-
260
- // 3. ADIM: Döndürülmüş Bitmap'i geçici bir dosyaya kaydet
261
- // (Bu kısım, cropImageWithUri'deki ile tamamen aynı)
262
- val mimeType = contentResolver.getType(sourceUri) ?: "image/jpeg"
263
- val fileExtension = when {
264
- mimeType.contains("png") -> "png"
265
- mimeType.contains("webp") -> "webp"
266
- else -> "jpg"
267
- }
268
-
269
- val cacheDir = reactContext.cacheDir
270
- val outputFile = createTempFile("rotated_", ".$fileExtension", cacheDir)
271
- val fileOutputStream = outputFile.outputStream()
272
-
273
- val compressFormat = when (fileExtension) {
274
- "png" -> Bitmap.CompressFormat.PNG
275
- "webp" -> if (android.os.Build.VERSION.SDK_INT >= 30) Bitmap.CompressFormat.WEBP_LOSSLESS else Bitmap.CompressFormat.WEBP
276
- else -> Bitmap.CompressFormat.JPEG
277
- }
246
+ fun RotateImageWithUri(fileUri: String, angle: Double, promise: Promise) {
247
+ Thread {
248
+ var workFile: File? = null
249
+ try {
250
+ // 1. Dosyayı yerel bir kopyaya al (İster URL olsun ister Local)
251
+ workFile = getWorkFile(fileUri)
252
+
253
+ // 2. Boyutları oku
254
+ val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
255
+ workFile.inputStream().use { BitmapFactory.decodeStream(it, null, options) }
256
+
257
+ // 3. OOM önlemi
258
+ val decodeOptions = BitmapFactory.Options().apply {
259
+ inSampleSize = calculateInSampleSize(options, 2048, 2048)
260
+ }
278
261
 
279
- fileOutputStream.use { stream ->
280
- rotatedBitmap.compress(compressFormat, 95, stream)
281
- }
262
+ // 4. Bitmap'i yükle
263
+ val originalBitmap = workFile.inputStream().use {
264
+ BitmapFactory.decodeStream(it, null, decodeOptions)
265
+ } ?: throw Exception("Bitmap decode edilemedi.")
282
266
 
267
+ // 5. Döndür
268
+ val matrix = Matrix().apply { postRotate(angle.toFloat()) }
269
+ val rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true)
283
270
 
284
- // 4. ADIM: Belleği temizle
285
- originalBitmap.recycle()
286
- rotatedBitmap.recycle()
271
+ // 6. Kaydet
272
+ val outputFile = File.createTempFile("rotated_", ".jpg", reactContext.cacheDir)
273
+ outputFile.outputStream().use {
274
+ rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)
275
+ }
287
276
 
277
+ // Bellek temizliği
278
+ originalBitmap.recycle()
279
+ rotatedBitmap.recycle()
288
280
 
289
- // 5. ADIM: Yeni oluşturulan dosyanın URI'sini döndür
290
- promise.resolve(Uri.fromFile(outputFile).toString())
281
+ promise.resolve(Uri.fromFile(outputFile).toString())
291
282
 
292
- } catch (e: OutOfMemoryError) {
293
- promise.reject("E_OOM_ROTATE", "Resim döndürülürken hafıza yetersiz kaldı.")
294
- } catch (e: Exception) {
295
- promise.reject("E_ROTATE_URI", "Döndürme hatası: ${e.message}")
296
- }
297
- }.start()
298
- }
283
+ } catch (e: Exception) {
284
+ promise.reject("E_ROTATE_URI", e.message)
285
+ } finally {
286
+ // KRİTİK: İndirilen/kopyalanan geçici dosyayı SİL
287
+ workFile?.delete()
288
+ }
289
+ }.start()
290
+ }
299
291
 
300
292
 
301
293
  @ReactMethod
@@ -393,139 +385,80 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
393
385
  }
394
386
 
395
387
  @ReactMethod
396
- fun CropImageWithUri(fileUri: String, crop: ReadableMap, promise: Promise) {
397
- Thread {
398
- var originalBitmap: Bitmap? = null
399
- var rotatedBitmap: Bitmap? = null
400
- var croppedBitmap: Bitmap? = null
401
-
402
- try {
403
- val sourceUri = Uri.parse(fileUri)
404
- val contentResolver = reactContext.contentResolver
405
-
406
- // --- 1. İYİLEŞTİRME: EXIF Döndürme Bilgisini Oku ---
407
- var orientation = 0
408
- try {
409
- // InputStream'i EXIF okumak için bir kere açıyoruz
410
- contentResolver.openInputStream(sourceUri)?.use { inputStream ->
411
- val exif = ExifInterface(inputStream)
412
- orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
413
- }
414
- } catch (e: Exception) {
415
- // EXIF okunamıyorsa (örn: PNG), görmezden gel.
416
- println("EXIF bilgisi okunamadı: ${e.message}")
417
- }
418
-
419
- // --- 2. İYİLEŞTİRME: OOM Riskini Azaltmak için BitmapFactory.Options ---
420
- // Önce, sadece resmin boyutlarını almak için `inJustDecodeBounds` kullanıyoruz.
421
- // Bu, resmi belleğe yüklemeden boyutlarını okumamızı sağlar.
422
- val boundsOptions = BitmapFactory.Options()
423
- boundsOptions.inJustDecodeBounds = true
424
- contentResolver.openInputStream(sourceUri)?.use { inputStream ->
425
- BitmapFactory.decodeStream(inputStream, null, boundsOptions)
426
- }
388
+ fun CropImageWithUri(fileUri: String, crop: ReadableMap, promise: Promise) {
389
+ Thread {
390
+ var workFile: File? = null
391
+ try {
392
+ // 1. Dosyayı yerele al
393
+ workFile = getWorkFile(fileUri)
427
394
 
428
- val originalWidth = boundsOptions.outWidth
429
- val originalHeight = boundsOptions.outHeight
430
-
431
- // Bellek optimizasyonu için bir `inSampleSize` hesaplayalım.
432
- // Örneğin, resim 2048 pikselden büyükse, onu küçülterek oku.
433
- val decodeOptions = BitmapFactory.Options()
434
- var inSampleSize = 1
435
- val maxDimension = 2048 // Uygulamanızın ihtiyacına göre bu değeri ayarlayın
436
- if (originalHeight > maxDimension || originalWidth > maxDimension) {
437
- val halfHeight: Int = originalHeight / 2
438
- val halfWidth: Int = originalWidth / 2
439
- while (halfHeight / inSampleSize >= maxDimension && halfWidth / inSampleSize >= maxDimension) {
440
- inSampleSize *= 2
441
- }
442
- }
443
- decodeOptions.inSampleSize = inSampleSize
444
-
445
- // Şimdi resmi, hesaplanan `inSampleSize` ile belleğe yüklüyoruz.
446
- val originalBitmap = contentResolver.openInputStream(sourceUri)?.use { inputStream ->
447
- BitmapFactory.decodeStream(inputStream, null, decodeOptions)
448
- } ?: throw Exception("Bitmap oluşturulamadı (decodeStream).")
449
-
450
-
451
- // --- 3. İYİLEŞTİRME: Döndürme Matrisini Oluştur ---
452
- val matrix = Matrix()
453
- // EXIF bilgisine göre döndürme matrisini ayarla
454
- when (orientation) {
455
- ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
456
- ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
457
- ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
458
- ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.preScale(-1.0f, 1.0f)
459
- ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1.0f, -1.0f)
460
- // Diğer karmaşık oryantasyonlar da eklenebilir...
461
- }
462
-
463
- // Döndürme işlemini uygula
464
- val rotatedBitmap =
465
- if (!matrix.isIdentity) {
466
- Bitmap.createBitmap(
467
- originalBitmap,0, 0, originalBitmap.width, originalBitmap.height, matrix, true
468
- ).also {
469
- originalBitmap.recycle()
470
- }
471
- } else {
472
- originalBitmap
473
- }
395
+ // 2. EXIF bilgisini yerel dosyadan oku (URL'de bu çalışmazdı, şimdi çalışır)
396
+ val orientation = try {
397
+ ExifInterface(workFile.absolutePath).getAttributeInt(
398
+ ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL
399
+ )
400
+ } catch (_: Exception) { ExifInterface.ORIENTATION_NORMAL }
474
401
 
402
+ // 3. Boyut ölçümü
403
+ val boundsOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true }
404
+ workFile.inputStream().use { BitmapFactory.decodeStream(it, null, boundsOptions) }
475
405
 
476
- // 4. ADIM: Kırpma koordinatlarını hesapla (Artık DÖNDÜRÜLMÜŞ bitmap üzerinden)
477
- val rx = crop.getDouble("x")
478
- val ry = crop.getDouble("y")
479
- val rw = crop.getDouble("width")
480
- val rh = crop.getDouble("height")
481
-
482
- // NOT: Artık rotatedBitmap'in boyutlarını kullanıyoruz!
483
- val px = (rx * rotatedBitmap.width).toInt().coerceIn(0, rotatedBitmap.width - 1)
484
- val py = (ry * rotatedBitmap.height).toInt().coerceIn(0, rotatedBitmap.height - 1)
485
- val pw = (rw * rotatedBitmap.width).toInt().coerceAtMost(rotatedBitmap.width - px).coerceAtLeast(1)
486
- val ph = (rh * rotatedBitmap.height).toInt().coerceAtMost(rotatedBitmap.height - py).coerceAtLeast(1)
406
+ val decodeOptions = BitmapFactory.Options().apply {
407
+ inSampleSize = calculateInSampleSize(boundsOptions, 2048, 2048)
408
+ }
487
409
 
410
+ // 4. Bitmap yükle
411
+ val originalBitmap = workFile.inputStream().use {
412
+ BitmapFactory.decodeStream(it, null, decodeOptions)
413
+ } ?: throw Exception("Bitmap yüklenemedi.")
414
+
415
+ // 5. EXIF Rotasyonu uygula
416
+ val matrix = Matrix()
417
+ when (orientation) {
418
+ ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
419
+ ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
420
+ ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
421
+ }
488
422
 
489
- // 5. ADIM: Bitmap'i kırp
490
- val croppedBitmap = Bitmap.createBitmap(rotatedBitmap, px, py, pw, ph)
423
+ val rotatedBitmap = if (!matrix.isIdentity) {
424
+ Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true)
425
+ .also { originalBitmap.recycle() }
426
+ } else originalBitmap
491
427
 
428
+ // 6. Kırpma koordinatları
429
+ val rx = crop.getDouble("x")
430
+ val ry = crop.getDouble("y")
431
+ val rw = crop.getDouble("width")
432
+ val rh = crop.getDouble("height")
492
433
 
493
- // 6. ADIM: Kırpılmış Bitmap'i geçici bir dosyaya kaydet
494
- // ... (Bu kısım önceki kodla aynı, değişmedi) ...
495
- val mimeType = contentResolver.getType(sourceUri) ?: "image/jpeg"
496
- val fileExtension = when {
497
- mimeType.contains("png") -> "png"
498
- mimeType.contains("webp") -> "webp"
499
- else -> "jpg"
500
- }
501
- val cacheDir = reactContext.cacheDir
502
- val outputFile = createTempFile("cropped_", ".$fileExtension", cacheDir)
503
- val fileOutputStream = outputFile.outputStream()
504
- val compressFormat = when (fileExtension) {
505
- "png" -> Bitmap.CompressFormat.PNG
506
- "webp" -> if (android.os.Build.VERSION.SDK_INT >= 30) Bitmap.CompressFormat.WEBP_LOSSLESS else Bitmap.CompressFormat.WEBP
507
- else -> Bitmap.CompressFormat.JPEG
508
- }
509
- fileOutputStream.use { stream ->
510
- croppedBitmap.compress(compressFormat, 95, stream)
511
- }
434
+ val px = (rx * rotatedBitmap.width).toInt().coerceIn(0, rotatedBitmap.width - 1)
435
+ val py = (ry * rotatedBitmap.height).toInt().coerceIn(0, rotatedBitmap.height - 1)
436
+ val pw = (rw * rotatedBitmap.width).toInt().coerceAtMost(rotatedBitmap.width - px).coerceAtLeast(1)
437
+ val ph = (rh * rotatedBitmap.height).toInt().coerceAtMost(rotatedBitmap.height - py).coerceAtLeast(1)
512
438
 
439
+ val croppedBitmap = Bitmap.createBitmap(rotatedBitmap, px, py, pw, ph)
513
440
 
514
- if (originalBitmap != rotatedBitmap) originalBitmap?.recycle()
515
- rotatedBitmap?.recycle()
516
- croppedBitmap?.recycle()
441
+ // 7. Çıktıyı kaydet
442
+ val outputFile = File.createTempFile("cropped_", ".jpg", reactContext.cacheDir)
443
+ outputFile.outputStream().use {
444
+ croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)
445
+ }
517
446
 
447
+ // Temizlik
448
+ if (originalBitmap != rotatedBitmap) originalBitmap.recycle()
449
+ rotatedBitmap.recycle()
450
+ croppedBitmap.recycle()
518
451
 
519
- // 8. ADIM: Yeni oluşturulan dosyanın URI'sini döndür
520
- promise.resolve(Uri.fromFile(outputFile).toString())
452
+ promise.resolve(Uri.fromFile(outputFile).toString())
521
453
 
522
- } catch (e: OutOfMemoryError) {
523
- promise.reject("E_OOM_CROP", "Resim işlenirken hafıza yetersiz kaldı.")
524
- } catch (e: Exception) {
525
- promise.reject("E_CROP_URI", "Kırpma hatası: ${e.message}")
526
- }
527
- }.start()
528
- }
454
+ } catch (e: Exception) {
455
+ promise.reject("E_CROP_URI", e.message)
456
+ } finally {
457
+ // KRİTİK: Geçici dosyayı SİL
458
+ workFile?.delete()
459
+ }
460
+ }.start()
461
+ }
529
462
 
530
463
  @ReactMethod
531
464
  fun DeleteTempFiles(fileUris: ReadableArray, promise: Promise) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-my-uploader-android",
3
- "version": "1.0.55",
3
+ "version": "1.0.56",
4
4
  "description": "file uploader for android",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./src/index.d.ts",