Attaching a file to a notebook

I believe the following program will do all you asked for. It will generate this little grid of buttons:

Mathematica graphics

You can use the "Add file" button as many times as you want to add as many files you want. Those files are stored in the notebook that contains this button grid, so you can copy the grid to an empty notebook and use the files without the need to execute any code. The other buttons do what you intended. You get a dialog to determine the specific internal file to export, open or delete.

DynamicModule[{files, fileNames, selectedFile, fileChosen, fileName, 
  tempFile, fileSelectDialog, afButton, dfButton, efButton, ofButton},

 files = {};
 fileNames = {};

 fileSelectDialog[] :=
  If[fileNames === {},
   selectedFile = $Canceled, 
   (*else*)
   selectedFile = First@fileNames;
   DialogInput[
    Column[
     {
      TextCell["Select File:"],
      PopupMenu[Dynamic[selectedFile], fileNames], "", 
      Row[{CancelButton[], "  ", DefaultButton[DialogReturn@selectedFile]}]
      }
     ]
    ]
   ];

 afButton[] :=
  Button["Add file",
   fileChosen = SystemDialogInput["FileOpen"];
   If[fileChosen =!= $Canceled,
    fileName = FileNameTake@fileChosen;
    AppendTo[files, Compress@Import[fileChosen, "Byte"]];
    AppendTo[fileNames, fileName];
    ];,
   Method -> "Queued"
   ];

 dfButton[] :=
  Button["Delete file",
   fileChosen = fileSelectDialog[];
   If[fileChosen =!= $Canceled && fileNames != {},
    files = Delete[files, First@First@Position[fileNames, fileChosen]]; 
    fileNames = DeleteCases[fileNames, fileChosen, 1, 1], (*else*)
    DialogInput[
      DialogNotebook[{TextCell["Nothing to delete"], 
        Button["Proceed", DialogReturn[1]]}]];
    ];
   , Method -> "Queued"
   ];

 efButton[] :=
  Button["Export file",
   fileChosen = fileSelectDialog[];
   If[fileChosen =!= $Canceled && fileNames != {},
    fileName = SystemDialogInput["FileSave", fileChosen];
    If[fileName =!= $Canceled,
      Export[fileName, Uncompress@First@Pick[files, fileNames, fileChosen], "Byte"]
    ], 
    (*else*)
    DialogInput[
      DialogNotebook[{TextCell["Nothing to export"], 
        Button["Proceed", DialogReturn[]]}]];
    ];,
   Method -> "Queued"
   ];

 ofButton[] :=
  Button["Open file",
   fileChosen = fileSelectDialog[];
   If[fileChosen =!= $Canceled && fileNames != {},
    tempFile = FileNameJoin[{$TemporaryDirectory, fileChosen}];
    SystemOpen@
     Export[tempFile, Uncompress@First@Pick[files, fileNames, fileChosen], "Byte"],
    (*else*)
    DialogInput[
      DialogNotebook[{TextCell["Nothing to open"], 
        Button["Proceed", DialogReturn[]]}]];
    ];,
   Method -> "Queued"
   ];

 Manipulate[
  Grid[{{afButton[], dfButton[]}, {efButton[], ofButton[]}}],
  SaveDefinitions -> True, TrackedSymbols -> {}
  ]
 ]

This is a bit crude, but seems to work so far: First you get the data into your notebook file via filedata = Import["kitten.jpg", "RawData"];. With this raw data you now have, you create a Base64 representation:

base64data = ExportString[filedata, "Base64"]

(this is the same algorithm which is used in emails for representing binary data as alphanumeric-only characters - on the cost that it takes about 1.5x the size of the original file.) This data you then put in a cell and write myFileData = before it. Then mark the cell as initialisation cell.

Button["Write and open file",
   BinaryWrite["newkitten.jpg", 
     ToExpression[ImportString[myFileData, "Base64"]]]; 
   SystemOpen["newkitten.jpg"]
]

The button will then create a new file in the current working directory and open it. If anyone has a few suggestions how to streamline the process of getting the Base64 data permanently saved in the notebook, please let me know.

Tags:

Notebooks