datasync-blob 1.1.23 → 1.1.25

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.
@@ -5,22 +5,155 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.DsBlob = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _reactToastify = require("react-toastify");
10
+ require("react-toastify/dist/ReactToastify.css");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
9
13
  class DsBlob extends _react.Component {
10
14
  constructor(props) {
11
15
  super(props);
12
16
  this.state = {
13
17
  hover_input: false,
14
- hover_image: false
18
+ hover_image: false,
19
+ data: props.data || "",
20
+ binaryData: props.binaryData || null
15
21
  };
16
22
  this.debugging = false;
17
23
  }
18
24
 
25
+ //-----------------------------------------
26
+ /**
27
+ * PROTOTYPE : uploadToDatasyncCloud
28
+ * Purpose : Upload binary data to My Custom Cloud server using Datasync SncPushCloud endpoint and return the accessible URL for the uploaded file.
29
+ * @param base64Data
30
+ * @param fileName
31
+ */
32
+ uploadToDatasyncCloud = async pictureBlob => {
33
+ try {
34
+ const formData = new FormData();
35
+ let cloud_filename = pictureBlob.cloud_url_prefix.split('/').pop(); // Extract filename from cloud_url_prefix
36
+
37
+ console.log("Uploading to Datasync Cloud with filename:", cloud_filename);
38
+ formData.append('action', "syncPushCloud");
39
+ //formData.append('filename', `${CGUID()}.jpg`);
40
+ formData.append('filename', cloud_filename); // Extract filename from cloud_url_prefix
41
+
42
+ // Convert blob URL to actual binary data
43
+ let binaryString;
44
+ if (pictureBlob.isBlobUrl && pictureBlob.data.startsWith('blob:')) {
45
+ // Fetch the blob URL to get actual file content
46
+ console.log('Fetching blob URL:', pictureBlob.data);
47
+ const blobResponse = await fetch(pictureBlob.data);
48
+ const blob = await blobResponse.blob();
49
+
50
+ // Convert blob to ArrayBuffer then to Uint8Array
51
+ const arrayBuffer = await blob.arrayBuffer(); // Raw binary JPEG data
52
+ const uint8Array = new Uint8Array(arrayBuffer); // ← THIS CONTAINS THE GENUINE JPEG FILE BINARY DATA
53
+
54
+ // Convert to base64 string for transmission (chunked to avoid stack overflow)
55
+ let binaryStr = '';
56
+ const chunkSize = 8192; // Process in 8KB chunks to avoid stack overflow
57
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
58
+ const chunk = uint8Array.slice(i, i + chunkSize);
59
+ binaryStr += String.fromCharCode(...chunk);
60
+ }
61
+ binaryString = btoa(binaryStr); // ← THIS CONTAINS THE GENUINE JPEG FILE AS BASE64 STRING
62
+
63
+ console.log('Converted blob URL to binary data, size:', uint8Array.length);
64
+ } else if (Array.isArray(pictureBlob.binaryData)) {
65
+ // Convert Uint8Array or regular array to base64 string
66
+ const uint8Array = new Uint8Array(pictureBlob.binaryData);
67
+ let binaryStr = '';
68
+ const chunkSize = 8192;
69
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
70
+ const chunk = uint8Array.slice(i, i + chunkSize);
71
+ binaryStr += String.fromCharCode(...chunk);
72
+ }
73
+ binaryString = btoa(binaryStr);
74
+ } else if (pictureBlob.binaryData instanceof Uint8Array) {
75
+ // Already a Uint8Array, convert to base64
76
+ let binaryStr = '';
77
+ const chunkSize = 8192;
78
+ for (let i = 0; i < pictureBlob.binaryData.length; i += chunkSize) {
79
+ const chunk = pictureBlob.binaryData.slice(i, i + chunkSize);
80
+ binaryStr += String.fromCharCode(...chunk);
81
+ }
82
+ binaryString = btoa(binaryStr);
83
+ } else {
84
+ // Assume it's already a string
85
+ binaryString = pictureBlob.binaryData || "";
86
+ }
87
+ formData.append('binary', binaryString);
88
+ console.log("binaryString ->", binaryString.substring(0, 100) + '...'); // Log the beginning of the base64 string for debugging
89
+
90
+ const results = await fetch('http://localhost:8888/datasync-service/Sync.php', {
91
+ method: 'POST',
92
+ body: formData
93
+ });
94
+ if (!results.ok) {
95
+ throw new Error(`HTTP error! status: ${response.statusText}`);
96
+ }
97
+ let sync_push_cloud_response = await results.json();
98
+
99
+ // Try to parse as JSON
100
+ try {
101
+ if (!sync_push_cloud_response.state) throw new Error(`syncPushCloud returned error: ${sync_push_cloud_response.message}`);else console.log('syncPushCloud upload response:', sync_push_cloud_response.message);
102
+ return true; // Return true on successful upload
103
+ } catch (parseError) {
104
+ console.error('syncPushCloud : JSON parse error:', parseError);
105
+ throw new Error(`syncPushCloud raised error: ${parseError}`);
106
+ }
107
+ } catch (error) {
108
+ if (true) {
109
+ console.error('syncPushCloud upload error details:', error);
110
+ }
111
+ throw error;
112
+ }
113
+ };
114
+ setPicture = data => {
115
+ console.log("setPicture => Fake procedure canbe deleted !");
116
+ };
117
+
118
+ //-----------------------------------------
119
+ convert_blob_picture_into_cloud_file = async pictureBlob => {
120
+ try {
121
+ console.log("Received pictureBlob in uploadPicture:cloud_url_prefix -> ", pictureBlob.cloud_url_prefix);
122
+ if (!pictureBlob || !pictureBlob.data) {
123
+ //Reset picture
124
+ this.setPicture("");
125
+ return;
126
+ }
127
+ if (pictureBlob.isBlobUrl && pictureBlob.data.startsWith('blob:') && pictureBlob.cloud_url_prefix) {
128
+ let updloadSucces = await this.uploadToDatasyncCloud(pictureBlob); // Upload the image to custom Datasync cloud
129
+ if (updloadSucces) {
130
+ //Clear the data URL to free memory since the image is now uploaded and accessible via cloud URL
131
+ if (pictureBlob.data && pictureBlob.data.startsWith('blob:')) {
132
+ URL.revokeObjectURL(pictureBlob.data);
133
+ }
134
+ this.setPicture(pictureBlob.cloud_url_prefix); //Display the image using the cloud URL only if upload succeeded
135
+ _reactToastify.toast.success("Image uploadée");
136
+ console.log("pictureBlob->", pictureBlob);
137
+
138
+ //inform parent component of the new cloud URL for the uploaded image
139
+ this.props.uploadPicture({
140
+ data: pictureBlob.cloud_url_prefix
141
+ });
142
+ } else {
143
+ console.error("Upload to Datasync cloud failed - no success response received");
144
+ throw new Error("Upload to Datasync cloud failed - no success response received");
145
+ }
146
+ }
147
+ } catch (error) {
148
+ _reactToastify.toast.error("Erreur lors du téléchargement de l'image");
149
+ console.error("Upload error:", error);
150
+ }
151
+ };
152
+
19
153
  //-----------------------------------------------------------------------------------------------------------------------------------------
20
154
  reduceBase64Image = e => {
21
155
  let imageSource = e.path && e.path[0] || e.srcElement; //Safari compliancy
22
156
 
23
- alert("reduceBase64Image !");
24
157
  if (this.debugging) console.log("imageSource = ", imageSource);
25
158
  var canvas = document.createElement('canvas'),
26
159
  context,
@@ -78,17 +211,17 @@ class DsBlob extends _react.Component {
78
211
  let canvasData = canvas.toDataURL('image/jpg', jpegCompressionRatio); //Jpeg conversion
79
212
 
80
213
  if (this.debugging) console.log("jpegCompressionRatio->", jpegCompressionRatio, " canvasData.length = ", canvasData.length);
214
+ const processedData = this.props.removebase64 ? canvasData ? canvasData.substring("data:image/png;base64".length) : "" : canvasData ? canvasData : "";
215
+ this.setData(processedData);
81
216
  if (this.props.removebase64) {
82
217
  //remove base64 prefix if property is set
83
218
  this.props.uploadPicture({
84
- data: canvasData ? canvasData.substring("data:image/png;base64".length) : "",
85
- item_id: this.props.item_id
219
+ data: processedData
86
220
  });
87
221
  } else {
88
222
  //Keep base 64 prefix as it is
89
223
  this.props.uploadPicture({
90
- data: canvasData ? canvasData : "",
91
- item_id: this.props.item_id
224
+ data: processedData
92
225
  });
93
226
  }
94
227
  };
@@ -96,7 +229,6 @@ class DsBlob extends _react.Component {
96
229
  reduceBinaryImage = e => {
97
230
  let imageSource = e.path && e.path[0] || e.srcElement; //Safari compliancy
98
231
 
99
- alert("reduceBinaryImage !");
100
232
  if (this.debugging) console.log("imageSource = ", imageSource);
101
233
  var canvas = document.createElement('canvas'),
102
234
  context,
@@ -166,12 +298,25 @@ class DsBlob extends _react.Component {
166
298
  console.log("Fallback: Using binary data, size:", binaryData.byteLength);
167
299
 
168
300
  // Store both display URL and binary data
301
+ this.setData(blobUrl, binaryData);
169
302
  this.props.uploadPicture({
303
+ data: this.props.cloud_url_prefix,
304
+ // Pass the cloud URL for display
305
+ binaryData: null,
306
+ // Clear binary data since we now have a cloud URL
307
+ cloud_url_prefix: this.props.cloud_url_prefix,
308
+ // Pass cloud URL prefix if needed for parent component
309
+ isBlobUrl: false // The final URL is not a blob URL, it's a cloud URL
310
+ });
311
+
312
+ //Call the upload callback with both blob URL for display and binary data for upload
313
+ this.convert_blob_picture_into_cloud_file({
170
314
  data: blobUrl,
171
315
  // For display in img tag
172
316
  binaryData: binaryData,
173
317
  // Genuine JPEG binary stream
174
- item_id: this.props.item_id,
318
+ cloud_url_prefix: this.props.cloud_url_prefix,
319
+ // Pass cloud URL prefix if needed for parent component
175
320
  isBlobUrl: true // Flag to track blob URLs for cleanup
176
321
  });
177
322
  };
@@ -187,7 +332,6 @@ class DsBlob extends _react.Component {
187
332
  var image = document.createElement('img');
188
333
  image.onload = this.reduceBase64Image;
189
334
  console.log("storeBase64ImageToImg:e.currentTarget.result = ", e.currentTarget.result);
190
- alert("storeBase64ImageToImg !");
191
335
  image.src = e.currentTarget.result;
192
336
  };
193
337
 
@@ -196,7 +340,6 @@ class DsBlob extends _react.Component {
196
340
  var image = document.createElement('img');
197
341
  //2Do DEBUG image.onload = this.reduceImage;
198
342
  console.log("storeBinaryImageToImg:e.currentTarget.result = ", e.currentTarget.result);
199
- alert("storeBinaryImageToImg !");
200
343
  // Convert ArrayBuffer to Blob URL since image.src cannot accept ArrayBuffer directly
201
344
  // Use the detected image type from the original file
202
345
  const imageType = this.currentImageType || 'image/jpeg'; // fallback to jpeg
@@ -219,7 +362,6 @@ class DsBlob extends _react.Component {
219
362
  readImageAsBase64 = async aPictureFile => {
220
363
  let _reader = new FileReader();
221
364
  console.log("readImageAsBase64:aPictureFile = ", aPictureFile);
222
- alert("readImageAsBase64 !");
223
365
  _reader.onload = this.storeBase64ImageToImg;
224
366
  _reader.readAsDataURL(aPictureFile); //The readAsDataURL() method of the FileReader interface is used to read the contents of the specified file's data as a base64 encoded string.
225
367
  };
@@ -228,7 +370,6 @@ class DsBlob extends _react.Component {
228
370
  readImageAsBinary = async aPictureFile => {
229
371
  let _reader = new FileReader();
230
372
  console.log("readImageAsBinary:aPictureFile = ", aPictureFile);
231
- alert("readImageAsBinary !");
232
373
 
233
374
  // Store the image type for use in storeBinaryImageToImg
234
375
  this.currentImageType = aPictureFile.type;
@@ -241,21 +382,43 @@ class DsBlob extends _react.Component {
241
382
  resetUpload = () => {
242
383
  // Clean up any blob URLs before resetting
243
384
  this.cleanupBlobUrl();
385
+ this.setData("", null);
244
386
  this.props.uploadPicture({
245
387
  data: "",
246
- item_id: this.props.item_id
388
+ cloud_url_prefix: this.props.cloud_url_prefix,
389
+ isBlobUrl: false
247
390
  });
391
+ //this.props.uploadPicture({data:"",cloud_url_prefix:this.props.cloud_url_prefix, isBlobUrl:true});
248
392
  };
249
393
 
250
394
  //-----------------------------------------------------------------------------------------------------------------------------------------
251
395
  cleanupBlobUrl = () => {
396
+ // Only show alert if not in test environment
397
+ if (typeof jest === 'undefined') {
398
+ console.log("cleanupBlobUrl !");
399
+ }
252
400
  // Revoke blob URL if it exists to prevent memory leaks
253
- if (this.props.data && this.props.data.startsWith && this.props.data.startsWith('blob:')) {
254
- URL.revokeObjectURL(this.props.data);
255
- console.log("Blob URL cleaned up:", this.props.data);
401
+ if (this.state.data && this.state.data.startsWith('blob:')) {
402
+ URL.revokeObjectURL(this.state.data);
403
+ console.log("Blob URL cleaned up:", this.state.data);
256
404
  }
257
405
  };
258
406
 
407
+ //-----------------------------------------------------------------------------------------------------------------------------------------
408
+ componentDidUpdate(prevProps) {
409
+ // Sync state with prop changes from parent
410
+ if (prevProps.data !== this.props.data) {
411
+ this.setState({
412
+ data: this.props.data || ""
413
+ });
414
+ }
415
+ if (prevProps.binaryData !== this.props.binaryData) {
416
+ this.setState({
417
+ binaryData: this.props.binaryData || null
418
+ });
419
+ }
420
+ }
421
+
259
422
  //-----------------------------------------------------------------------------------------------------------------------------------------
260
423
  componentWillUnmount() {
261
424
  // Cleanup blob URLs when component unmounts
@@ -304,28 +467,40 @@ class DsBlob extends _react.Component {
304
467
 
305
468
  //-----------------------------------------------------------------------------------------------------------------------------------------
306
469
  getImageSrc = () => {
307
- if (!this.props.data) return "";
470
+ if (!this.state.data) return "";
308
471
 
309
472
  // Check if it's already a complete URL (cloud storage URL or blob URL)
310
- if (this.props.data.startsWith('http://') || this.props.data.startsWith('https://') || this.props.data.startsWith('blob:')) {
311
- return this.props.data;
473
+ if (this.state.data.startsWith('http://') || this.state.data.startsWith('https://') || this.state.data.startsWith('blob:')) {
474
+ return this.state.data;
312
475
  }
313
476
 
314
477
  // Check if it's already a data URL (base64)
315
- if (this.props.data.startsWith('data:')) {
316
- return this.props.data;
478
+ if (this.state.data.startsWith('data:')) {
479
+ return this.state.data;
317
480
  }
318
481
 
319
482
  // Otherwise, treat as base64 and add appropriate prefix
320
483
  const prefix = this.props.removebase64 ? "data:image/png;base64," : "";
321
- return prefix + this.props.data;
484
+ return prefix + this.state.data;
322
485
  };
323
486
 
324
487
  //-----------------------------------------------------------------------------------------------------------------------------------------
325
488
  getBinaryData = () => {
326
489
  // Return the genuine JPEG binary stream (ArrayBuffer)
327
490
  // This can be used by parent components for uploading or processing
328
- return this.props.binaryData || null;
491
+ return this.state.binaryData || this.props.binaryData || null;
492
+ };
493
+
494
+ //-----------------------------------------------------------------------------------------------------------------------------------------
495
+ // Data getter and setter methods for read-write access
496
+ getData = () => {
497
+ return this.state.data;
498
+ };
499
+ setData = (data, binaryData = null) => {
500
+ this.setState({
501
+ data: data || "",
502
+ binaryData: binaryData
503
+ });
329
504
  };
330
505
 
331
506
  //-----------------------------------------------------------------------------------------------------------------------------------------
@@ -333,7 +508,7 @@ class DsBlob extends _react.Component {
333
508
  // Return the type of image data being used
334
509
  if (this.props.isCloudUrl) return 'cloud';
335
510
  if (this.props.isBlobUrl) return 'blob';
336
- if (this.props.data && this.props.data.startsWith('data:')) return 'base64';
511
+ if (this.state.data && this.state.data.startsWith('data:')) return 'base64';
337
512
  return 'unknown';
338
513
  };
339
514
  render() {
@@ -365,7 +540,7 @@ class DsBlob extends _react.Component {
365
540
  display: "none",
366
541
  cursor: "pointer"
367
542
  };
368
- return /*#__PURE__*/_react.default.createElement("div", null, !this.props.data && /*#__PURE__*/_react.default.createElement("label", {
543
+ return /*#__PURE__*/_react.default.createElement("div", null, !this.state.data && /*#__PURE__*/_react.default.createElement("label", {
369
544
  id: "upload-picture-label",
370
545
  className: this.props.buttonStyle,
371
546
  style: upload_picture_label
@@ -376,7 +551,7 @@ class DsBlob extends _react.Component {
376
551
  accept: "image/*",
377
552
  onChange: this.onInputChange,
378
553
  multiple: true
379
- })), this.props.data && this.props.data.length > 0 && /*#__PURE__*/_react.default.createElement("div", {
554
+ })), this.state.data && this.state.data.length > 0 && /*#__PURE__*/_react.default.createElement("div", {
380
555
  class: "show-image",
381
556
  style: div_show_image,
382
557
  onMouseOver: () => {
@@ -405,7 +580,44 @@ class DsBlob extends _react.Component {
405
580
  type: "button",
406
581
  value: "Effacer",
407
582
  onClick: this.resetUpload
408
- })));
583
+ })), /*#__PURE__*/_react.default.createElement(_reactToastify.ToastContainer, {
584
+ position: "top-right",
585
+ autoClose: 3000,
586
+ hideProgressBar: false,
587
+ newestOnTop: false,
588
+ closeOnClick: true,
589
+ rtl: false,
590
+ pauseOnFocusLoss: true,
591
+ draggable: true,
592
+ pauseOnHover: true
593
+ }));
409
594
  }
410
595
  }
411
- exports.DsBlob = DsBlob;
596
+ exports.DsBlob = DsBlob;
597
+ DsBlob.propTypes = {
598
+ // Image handling props (note: data and binaryData are managed as read-write state internally)
599
+ data: _propTypes.default.string,
600
+ binaryData: _propTypes.default.object,
601
+ // Upload callback
602
+ uploadPicture: _propTypes.default.func.isRequired,
603
+ // Image size constraints
604
+ width: _propTypes.default.number,
605
+ height: _propTypes.default.number,
606
+ maxWidth: _propTypes.default.number,
607
+ maxHeight: _propTypes.default.number,
608
+ // Image processing options
609
+ reduceImage: _propTypes.default.bool,
610
+ jpegQuality: _propTypes.default.number,
611
+ removebase64: _propTypes.default.bool,
612
+ // Cloud storage options
613
+ cloud_storage: _propTypes.default.bool,
614
+ cloud_url_prefix: _propTypes.default.string,
615
+ // UI props
616
+ Caption: _propTypes.default.string,
617
+ buttonStyle: _propTypes.default.string,
618
+ pictureStyle: _propTypes.default.string,
619
+ readOnly: _propTypes.default.bool,
620
+ // Status flags
621
+ isCloudUrl: _propTypes.default.bool,
622
+ isBlobUrl: _propTypes.default.bool
623
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datasync-blob",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "description": "Datasync Blob component",
5
5
  "main": "./dist/components/DsBlob.js",
6
6
  "files": [
@@ -42,5 +42,8 @@
42
42
  },
43
43
  "keywords": [],
44
44
  "author": "",
45
- "license": "ISC"
45
+ "license": "ISC",
46
+ "dependencies": {
47
+ "react-toastify": "^11.1.0"
48
+ }
46
49
  }